From b1f61ad74bb519bbe97aabfe9b2f04af9991d041 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Mon, 9 May 2022 17:01:35 +0100 Subject: [PATCH 001/806] Add `task::Waker::noop` --- library/core/src/task/wake.rs | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 27af227a1f27f..bf36d3ba2d674 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::fmt; use crate::marker::{PhantomData, Unpin}; +use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] /// which provides customized wakeup behavior. @@ -277,6 +278,45 @@ impl Waker { Waker { waker } } + /// Creates a new `Waker` that does nothing when `wake` is called. + /// + /// This is mostly useful for writing tests that need a [`Context`] to poll + /// some futures, but are not expecting those futures to wake the waker or + /// do not need to do anything specific if it happens. + /// + /// # Examples + /// + /// ``` + /// #![feature(noop_waker)] + /// + /// use std::future::Future; + /// use std::task; + /// + /// let waker = task::Waker::noop(); + /// let mut cx = task::Context::from_waker(&waker); + /// + /// let mut future = Box::pin(async { 10 }); + /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "noop_waker", issue = "none")] + pub const fn noop() -> Waker { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RAW, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); + + Waker { waker: RAW } + } + /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] From 1818ed7cfe8182c14cf05265d7af21ebd987b71e Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Mon, 20 Jun 2022 11:58:10 +0100 Subject: [PATCH 002/806] Add tracking issue for `noop_waker` --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index bf36d3ba2d674..3543e9655fade 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -300,7 +300,7 @@ impl Waker { /// ``` #[inline] #[must_use] - #[unstable(feature = "noop_waker", issue = "none")] + #[unstable(feature = "noop_waker", issue = "98286")] pub const fn noop() -> Waker { const VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker From e66d0208bc5b109c2e187aa57a95ec91ae57654a Mon Sep 17 00:00:00 2001 From: Michael van Straten Date: Fri, 10 Mar 2023 22:16:23 +0100 Subject: [PATCH 003/806] Fixed rust-analyser: no implementation for position() --- crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs index d258a02472909..e46d51f7b0594 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs @@ -20,7 +20,7 @@ use token_stream::TokenStreamBuilder; mod symbol; pub use symbol::*; -use std::ops::Bound; +use std::ops::{Bound, Range}; use crate::tt; @@ -298,6 +298,10 @@ impl server::Span for RustAnalyzer { // FIXME handle span span } + fn position(&mut self, _span: Self::Span) -> Range { + // FIXME handle span + Range { start: 0, end: 0 } + } fn start(&mut self, _span: Self::Span) -> LineColumn { // FIXME handle span LineColumn { line: 0, column: 0 } From e89d7dfe39c10aefcee285b8012d8ae68ad44830 Mon Sep 17 00:00:00 2001 From: Michael van Straten Date: Sat, 11 Mar 2023 12:14:06 +0100 Subject: [PATCH 004/806] Renamed to byte_range and changed Range generics [skip ci] --- crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs index e46d51f7b0594..a9cd8e705a4cf 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs @@ -298,7 +298,7 @@ impl server::Span for RustAnalyzer { // FIXME handle span span } - fn position(&mut self, _span: Self::Span) -> Range { + fn byte_range(&mut self, _span: Self::Span) -> Range { // FIXME handle span Range { start: 0, end: 0 } } From 7fb34c99e32c01082110dc7281049e26622f924d Mon Sep 17 00:00:00 2001 From: Dante Broggi <34220985+Dante-Broggi@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:52:32 -0400 Subject: [PATCH 005/806] add `#[doc(alias="flatmap")]` to `Option::and_then` I keep forgetting that rust calls this `and_then` and trying to search for `flatmap`. `and_then`'s docs even mention "Some languages call this operation flatmap", but it doesn't show up as a result in the search at `https://doc.rust-lang.org/std/?search=flatmap` --- library/core/src/option.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0f2475a8bdea6..b5ea891627532 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1464,6 +1464,7 @@ impl Option { /// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0)); /// assert_eq!(item_2_0, None); /// ``` + #[doc(alias = "flatmap")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_option_ext", issue = "91930")] From 66636939a63237fb7a6d1198dd9f92514807621e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 16 Mar 2023 16:26:19 +0100 Subject: [PATCH 006/806] feat: Pop a notification prompting the user to add a Cargo.toml of unlinked file to the linkedProjects --- crates/ide-diagnostics/src/lib.rs | 1 + crates/rust-analyzer/src/global_state.rs | 4 +- crates/rust-analyzer/src/reload.rs | 3 +- editors/code/package.json | 5 ++ editors/code/src/client.ts | 74 ++++++++++++++++++++---- editors/code/src/ctx.ts | 5 +- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 71f136b8c9030..0dc5343f9429d 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -74,6 +74,7 @@ use ide_db::{ }; use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange}; +// FIXME: Make this an enum #[derive(Copy, Clone, Debug, PartialEq)] pub struct DiagnosticCode(pub &'static str); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index aca6c92357070..649f856db4f2a 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -331,7 +331,7 @@ impl GlobalState { } pub(crate) fn send_notification( - &mut self, + &self, params: N::Params, ) { let not = lsp_server::Notification::new(N::METHOD.to_string(), params); @@ -372,7 +372,7 @@ impl GlobalState { self.req_queue.incoming.is_completed(&request.id) } - fn send(&mut self, message: lsp_server::Message) { + fn send(&self, message: lsp_server::Message) { self.sender.send(message).unwrap() } } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 1a6e1af2eb7ed..247d5b0438018 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -112,7 +112,8 @@ impl GlobalState { && self.config.notifications().cargo_toml_not_found { status.health = lsp_ext::Health::Warning; - message.push_str("Failed to discover workspace.\n\n"); + message.push_str("Failed to discover workspace.\n"); + message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n"); } for ws in self.workspaces.iter() { diff --git a/editors/code/package.json b/editors/code/package.json index c5eb08748bfab..d0c77db7b87fe 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -444,6 +444,11 @@ "type": "string" } }, + "rust-analyzer.showUnlinkedFileNotification": { + "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.", + "default": true, + "type": "boolean" + }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 565cb9c6432f4..2a1c757dfefd7 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -8,6 +8,7 @@ import * as diagnostics from "./diagnostics"; import { WorkspaceEdit } from "vscode"; import { Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; +import { sep as pathSeparator } from "path"; export interface Env { [name: string]: string; @@ -69,7 +70,8 @@ export async function createClient( outputChannel: vscode.OutputChannel, initializationOptions: vscode.WorkspaceConfiguration, serverOptions: lc.ServerOptions, - config: Config + config: Config, + unlinkedFiles: vscode.Uri[] ): Promise { const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: "file", language: "rust" }], @@ -119,6 +121,65 @@ export async function createClient( const preview = config.previewRustcOutput; const errorCode = config.useRustcErrorCode; diagnosticList.forEach((diag, idx) => { + let value = + typeof diag.code === "string" || typeof diag.code === "number" + ? diag.code + : diag.code?.value; + if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) { + let config = vscode.workspace.getConfiguration("rust-analyzer"); + if (config.get("showUnlinkedFileNotification")) { + unlinkedFiles.push(uri); + let folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; + if (folder) { + let parent_backslash = uri.fsPath.lastIndexOf( + pathSeparator + "src" + ); + let parent = uri.fsPath.substring(0, parent_backslash); + + if (parent.startsWith(folder)) { + let path = vscode.Uri.file( + parent + pathSeparator + "Cargo.toml" + ); + void vscode.workspace.fs.stat(path).then(() => { + vscode.window + .showInformationMessage( + `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`, + "Yes", + "No", + "Don't show this again" + ) + .then((choice) => { + switch (choice) { + case "Yes": + break; + case "No": + config.update( + "linkedProjects", + config + .get("linkedProjects") + ?.concat( + path.fsPath.substring( + folder!.length + ) + ), + false + ); + break; + case "Don't show this again": + config.update( + "showUnlinkedFileNotification", + false, + false + ); + break; + } + }); + }); + } + } + } + } + // Abuse the fact that VSCode leaks the LSP diagnostics data field through the // Diagnostic class, if they ever break this we are out of luck and have to go // back to the worst diagnostics experience ever:) @@ -138,14 +199,6 @@ export async function createClient( .substring(0, index) .replace(/^ -->[^\n]+\n/m, ""); } - let value; - if (errorCode) { - if (typeof diag.code === "string" || typeof diag.code === "number") { - value = diag.code; - } else { - value = diag.code?.value; - } - } diag.code = { target: vscode.Uri.from({ scheme: diagnostics.URI_SCHEME, @@ -153,7 +206,8 @@ export async function createClient( fragment: uri.toString(), query: idx.toString(), }), - value: value ?? "Click for full compiler diagnostic", + value: + errorCode && value ? value : "Click for full compiler diagnostic", }; } }); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index c2dca733df8f5..5515921ed1492 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -82,6 +82,7 @@ export class Ctx { private state: PersistentState; private commandFactories: Record; private commandDisposables: Disposable[]; + private unlinkedFiles: vscode.Uri[]; get client() { return this._client; @@ -99,6 +100,7 @@ export class Ctx { this.clientSubscriptions = []; this.commandDisposables = []; this.commandFactories = commandFactories; + this.unlinkedFiles = []; this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); @@ -218,7 +220,8 @@ export class Ctx { this.outputChannel, initializationOptions, serverOptions, - this.config + this.config, + this.unlinkedFiles ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => From b7b9ae59a081f0ecbd11d66e205307328cd5cfbe Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 8 Mar 2023 20:58:52 +0330 Subject: [PATCH 007/806] desugar `? operator` --- crates/hir-def/src/body.rs | 5 +- crates/hir-def/src/body/lower.rs | 93 +++++++++++++++++-- crates/hir-def/src/body/pretty.rs | 4 - crates/hir-def/src/expr.rs | 4 - crates/hir-def/src/lang_item.rs | 21 +++-- crates/hir-def/src/path.rs | 83 ++++++++++++----- crates/hir-def/src/path/lower.rs | 9 +- crates/hir-def/src/resolver.rs | 48 ++++++++-- crates/hir-ty/src/consteval.rs | 8 +- crates/hir-ty/src/consteval/tests.rs | 67 +++++++++++++ crates/hir-ty/src/db.rs | 4 + crates/hir-ty/src/diagnostics/unsafe_check.rs | 2 +- crates/hir-ty/src/infer.rs | 36 ++++--- crates/hir-ty/src/infer/expr.rs | 30 +++--- crates/hir-ty/src/infer/path.rs | 2 +- crates/hir-ty/src/lower.rs | 88 ++++++++++++------ crates/hir-ty/src/mir/eval.rs | 62 +++++++------ crates/hir-ty/src/mir/lower.rs | 18 +++- crates/hir-ty/src/mir/lower/as_place.rs | 2 +- crates/hir-ty/src/mir/pretty.rs | 1 + crates/hir-ty/src/utils.rs | 2 +- crates/hir/src/semantics.rs | 5 +- crates/hir/src/source_analyzer.rs | 34 +++---- .../handlers/replace_try_expr_with_match.rs | 6 +- crates/ide-assists/src/tests/generated.rs | 2 +- crates/ide/src/hover/tests.rs | 8 +- crates/test-utils/src/minicore.rs | 64 ++++++++++++- 27 files changed, 517 insertions(+), 191 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index b70e658efd79c..7f9b9476dcd91 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -391,7 +391,7 @@ impl Body { } }; let expander = Expander::new(db, file_id, module); - let (mut body, source_map) = Body::new(db, expander, params, body); + let (mut body, source_map) = Body::new(db, expander, params, body, module.krate); body.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) @@ -420,8 +420,9 @@ impl Body { expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, + krate: CrateId, ) -> (Body, BodySourceMap) { - lower::lower(db, expander, params, body) + lower::lower(db, expander, params, body, krate) } fn shrink_to_fit(&mut self) { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index fedaf39559858..348b7589ff4f4 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -3,6 +3,7 @@ use std::{mem, sync::Arc}; +use base_db::CrateId; use either::Either; use hir_expand::{ ast_id_map::AstIdMap, @@ -36,6 +37,7 @@ use crate::{ RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, + lang_item::LangItem, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, @@ -80,9 +82,11 @@ pub(super) fn lower( expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, + krate: CrateId, ) -> (Body, BodySourceMap) { ExprCollector { db, + krate, source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id), body: Body { @@ -107,6 +111,7 @@ struct ExprCollector<'a> { expander: Expander, ast_id_map: Arc, body: Body, + krate: CrateId, source_map: BodySourceMap, is_lowering_assignee_expr: bool, is_lowering_generator: bool, @@ -176,8 +181,7 @@ impl ExprCollector<'_> { self.source_map.expr_map.insert(src, id); id } - // desugared exprs don't have ptr, that's wrong and should be fixed - // somehow. + // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow. fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { self.body.exprs.alloc(expr) } @@ -199,6 +203,10 @@ impl ExprCollector<'_> { self.source_map.pat_map.insert(src, id); id } + // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow. + fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId { + self.body.pats.alloc(pat) + } fn missing_pat(&mut self) -> PatId { self.body.pats.alloc(Pat::Missing) } @@ -437,10 +445,7 @@ impl ExprCollector<'_> { let expr = self.collect_expr_opt(e.expr()); self.alloc_expr(Expr::Await { expr }, syntax_ptr) } - ast::Expr::TryExpr(e) => { - let expr = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::Try { expr }, syntax_ptr) - } + ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e), ast::Expr::CastExpr(e) => { let expr = self.collect_expr_opt(e.expr()); let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); @@ -601,6 +606,82 @@ impl ExprCollector<'_> { }) } + fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { + let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: { + if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) { + if let Some(cf_continue) = + LangItem::ControlFlowContinue.path(self.db, self.krate) + { + if let Some(cf_break) = + LangItem::ControlFlowBreak.path(self.db, self.krate) + { + if let Some(try_from_residual) = + LangItem::TryTraitFromResidual.path(self.db, self.krate) + { + break 'if_chain ( + try_branch, + cf_continue, + cf_break, + try_from_residual, + ); + } + } + } + } + // Some of the needed lang items are missing, so we can't desugar + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let operand = self.collect_expr_opt(e.expr()); + let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone()); + let expr = self.alloc_expr( + Expr::Call { + callee: try_branch, + args: Box::new([operand]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let continue_binding = + self.alloc_binding(name![v1], BindingAnnotation::Unannotated); + let continue_bpat = + self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None }); + self.add_definition_to_binding(continue_binding, continue_bpat); + let continue_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::TupleStruct { + path: Some(Box::new(cf_continue)), + args: Box::new([continue_bpat]), + ellipsis: None, + }), + guard: None, + expr: self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()), + }; + let break_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated); + let break_bpat = + self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); + self.add_definition_to_binding(break_binding, break_bpat); + let break_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::TupleStruct { + path: Some(Box::new(cf_break)), + args: Box::new([break_bpat]), + ellipsis: None, + }), + guard: None, + expr: { + let x = + self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()); + let callee = + self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); + let result = self.alloc_expr( + Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, + syntax_ptr.clone(), + ); + self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone()) + }, + }; + let arms = Box::new([continue_arm, break_arm]); + self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) + } + fn collect_macro_call( &mut self, mcall: ast::MacroCall, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 5a9b825a2530b..c091ad0d150f6 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -288,10 +288,6 @@ impl<'a> Printer<'a> { self.print_expr(*expr); w!(self, ".await"); } - Expr::Try { expr } => { - self.print_expr(*expr); - w!(self, "?"); - } Expr::Cast { expr, type_ref } => { self.print_expr(*expr); w!(self, " as "); diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index 19fa6b25419e1..5b8758224371b 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -192,9 +192,6 @@ pub enum Expr { Await { expr: ExprId, }, - Try { - expr: ExprId, - }, Cast { expr: ExprId, type_ref: Interned, @@ -383,7 +380,6 @@ impl Expr { } Expr::Field { expr, .. } | Expr::Await { expr } - | Expr::Try { expr } | Expr::Cast { expr, .. } | Expr::Ref { expr, .. } | Expr::UnaryOp { expr, .. } diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 4096e0a38267e..818054188bea3 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -8,8 +8,8 @@ use rustc_hash::FxHashMap; use syntax::SmolStr; use crate::{ - db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, - ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, + FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -220,11 +220,6 @@ macro_rules! language_item_table { } } - /// Opposite of [`LangItem::name`] - pub fn from_name(name: &hir_expand::name::Name) -> Option { - Self::from_str(name.as_str()?) - } - /// Opposite of [`LangItem::name`] pub fn from_str(name: &str) -> Option { match name { @@ -236,6 +231,18 @@ macro_rules! language_item_table { } } +impl LangItem { + /// Opposite of [`LangItem::name`] + pub fn from_name(name: &hir_expand::name::Name) -> Option { + Self::from_str(name.as_str()?) + } + + pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option { + let t = db.lang_item(start_crate, *self)?; + Some(Path::LangItem(t)) + } +} + language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index f3197d1800f22..c67c29818f5b1 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs @@ -8,6 +8,7 @@ use std::{ use crate::{ body::LowerCtx, + lang_item::LangItemTarget, type_ref::{ConstRefOrPath, LifetimeRef}, }; use hir_expand::name::Name; @@ -36,13 +37,19 @@ impl Display for ImportAlias { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Path { - /// Type based path like `::foo`. - /// Note that paths like `::foo` are desugared to `Trait::::foo`. - type_anchor: Option>, - mod_path: Interned, - /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. - generic_args: Option>]>>, +pub enum Path { + /// A normal path + Normal { + /// Type based path like `::foo`. + /// Note that paths like `::foo` are desugared to `Trait::::foo`. + type_anchor: Option>, + mod_path: Interned, + /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. + generic_args: Option>]>>, + }, + /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these + /// links via a normal path since they might be private and not accessible in the usage place. + LangItem(LangItemTarget), } /// Generic arguments to a path segment (e.g. the `i32` in `Option`). This @@ -102,51 +109,77 @@ impl Path { ) -> Path { let generic_args = generic_args.into(); assert_eq!(path.len(), generic_args.len()); - Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) } + Path::Normal { + type_anchor: None, + mod_path: Interned::new(path), + generic_args: Some(generic_args), + } + } + + /// Converts a known mod path to `Path`. + pub fn from_known_path_with_no_generic(path: ModPath) -> Path { + Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None } } pub fn kind(&self) -> &PathKind { - &self.mod_path.kind + match self { + Path::Normal { mod_path, .. } => &mod_path.kind, + Path::LangItem(_) => &PathKind::Abs, + } } pub fn type_anchor(&self) -> Option<&TypeRef> { - self.type_anchor.as_deref() + match self { + Path::Normal { type_anchor, .. } => type_anchor.as_deref(), + Path::LangItem(_) => None, + } } pub fn segments(&self) -> PathSegments<'_> { - let s = PathSegments { - segments: self.mod_path.segments(), - generic_args: self.generic_args.as_deref(), + let Path::Normal { mod_path, generic_args, .. } = self else { + return PathSegments { + segments: &[], + generic_args: None, + }; }; + let s = + PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() }; if let Some(generic_args) = s.generic_args { assert_eq!(s.segments.len(), generic_args.len()); } s } - pub fn mod_path(&self) -> &ModPath { - &self.mod_path + pub fn mod_path(&self) -> Option<&ModPath> { + match self { + Path::Normal { mod_path, .. } => Some(&mod_path), + Path::LangItem(_) => None, + } } pub fn qualifier(&self) -> Option { - if self.mod_path.is_ident() { + let Path::Normal { mod_path, generic_args, type_anchor } = self else { + return None; + }; + if mod_path.is_ident() { return None; } - let res = Path { - type_anchor: self.type_anchor.clone(), + let res = Path::Normal { + type_anchor: type_anchor.clone(), mod_path: Interned::new(ModPath::from_segments( - self.mod_path.kind, - self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(), + mod_path.kind, + mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(), )), - generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), + generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), }; Some(res) } pub fn is_self_type(&self) -> bool { - self.type_anchor.is_none() - && self.generic_args.as_deref().is_none() - && self.mod_path.is_Self() + let Path::Normal { mod_path, generic_args, type_anchor } = self else { + return false; + }; + type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self() } } @@ -222,7 +255,7 @@ impl GenericArgs { impl From for Path { fn from(name: Name) -> Path { - Path { + Path::Normal { type_anchor: None, mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))), generic_args: None, diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index b7542bd777d04..407f38daad4f5 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -75,8 +75,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option>::Foo desugars to Trait::Foo Some(trait_ref) => { - let Path { mod_path, generic_args: path_generic_args, .. } = - Path::from_src(trait_ref.path()?, ctx)?; + let Path::Normal { mod_path, generic_args: path_generic_args, .. } = + Path::from_src(trait_ref.path()?, ctx)? else + { + return None; + }; let num_segments = mod_path.segments().len(); kind = mod_path.kind; @@ -157,7 +160,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option Option<(TypeNs, Option)> { + let path = match path { + Path::Normal { mod_path, .. } => mod_path, + Path::LangItem(l) => { + return Some(( + match *l { + LangItemTarget::Union(x) => TypeNs::AdtId(x.into()), + LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x), + LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()), + LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x), + LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()), + LangItemTarget::Trait(x) => TypeNs::TraitId(x), + LangItemTarget::Function(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::Static(_) => return None, + }, + None, + )) + } + }; let first_name = path.segments().first()?; let skip_to_mod = path.kind != PathKind::Plain; if skip_to_mod { @@ -217,7 +237,7 @@ impl Resolver { pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; if unresolved.is_some() { @@ -245,8 +265,24 @@ impl Resolver { pub fn resolve_path_in_value_ns( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { + let path = match path { + Path::Normal { mod_path, .. } => mod_path, + Path::LangItem(l) => { + return Some(ResolveValueResult::ValueNs(match *l { + LangItemTarget::Function(x) => ValueNs::FunctionId(x), + LangItemTarget::Static(x) => ValueNs::StaticId(x), + LangItemTarget::Struct(x) => ValueNs::StructId(x), + LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + })) + } + }; let n_segments = path.segments().len(); let tmp = name![self]; let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; @@ -340,7 +376,7 @@ impl Resolver { pub fn resolve_path_in_value_ns_fully( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { match self.resolve_path_in_value_ns(db, path)? { ResolveValueResult::ValueNs(it) => Some(it), @@ -441,7 +477,7 @@ impl Resolver { &Scope::ImplDefScope(impl_) => { if let Some(target_trait) = &db.impl_data(impl_).target_trait { if let Some(TypeNs::TraitId(trait_)) = - self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path()) + self.resolve_path_in_type_ns_fully(db, &target_trait.path) { traits.insert(trait_); } diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 5830c48988fe5..fcb3445a54270 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -4,7 +4,7 @@ use base_db::CrateId; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; use hir_def::{ expr::Expr, - path::ModPath, + path::Path, resolver::{Resolver, ValueNs}, type_ref::ConstRef, ConstId, EnumVariantId, @@ -72,7 +72,7 @@ impl From for ConstEvalError { pub(crate) fn path_to_const( db: &dyn HirDatabase, resolver: &Resolver, - path: &ModPath, + path: &Path, mode: ParamLoweringMode, args_lazy: impl FnOnce() -> Generics, debruijn: DebruijnIndex, @@ -89,7 +89,7 @@ pub(crate) fn path_to_const( Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), None => { never!( - "Generic list doesn't contain this param: {:?}, {}, {:?}", + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", args, path, p @@ -228,7 +228,7 @@ pub(crate) fn eval_to_const( let db = ctx.db; if let Expr::Path(p) = &ctx.body.exprs[expr] { let resolver = &ctx.resolver; - if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) { + if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn) { return c; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 6a29e8ce52e62..3bec2ee88bfa3 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -801,6 +801,73 @@ fn options() { ); } +#[test] +fn from_trait() { + check_number( + r#" + //- minicore: from + struct E1(i32); + struct E2(i32); + + impl From for E2 { + fn from(E1(x): E1) -> Self { + E2(1000 * x) + } + } + const GOAL: i32 = { + let x: E2 = E1(2).into(); + x.0 + }; + "#, + 2000, + ); +} + +#[test] +fn try_operator() { + check_number( + r#" + //- minicore: option, try + const fn f(x: Option, y: Option) -> Option { + Some(x? * y?) + } + const fn g(x: Option, y: Option) -> i32 { + match f(x, y) { + Some(k) => k, + None => 5, + } + } + const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None); + "#, + 215, + ); + check_number( + r#" + //- minicore: result, try, from + struct E1(i32); + struct E2(i32); + + impl From for E2 { + fn from(E1(x): E1) -> Self { + E2(1000 * x) + } + } + + const fn f(x: Result) -> Result { + Ok(x? * 10) + } + const fn g(x: Result) -> i32 { + match f(x) { + Ok(k) => 7 * k, + Err(E2(k)) => 5 * k, + } + } + const GOAL: i32 = g(Ok(2)) + g(Err(E1(3))); + "#, + 15140, + ); +} + #[test] fn or_pattern() { check_number( diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 304c78767f129..8f1af4c2f8e2d 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -97,6 +97,10 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders]>; + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] + #[salsa::transparent] + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; + #[salsa::invoke(crate::lower::trait_environment_query)] fn trait_environment(&self, def: GenericDefId) -> Arc; diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index d25c0ccf00dcd..664822ee6fb4d 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -73,7 +73,7 @@ fn walk_unsafe( } Expr::Path(path) => { let resolver = resolver_for_expr(db.upcast(), def, current); - let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); + let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { if db.static_data(id).mutable { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 7de5b4295fcc4..00b5f7948ac4c 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -25,16 +25,16 @@ use hir_def::{ expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, - path::Path, + path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, - AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, - ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup, + TraitId, TypeAliasId, VariantId, }; use hir_expand::name::{name, Name}; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; -use stdx::always; +use stdx::{always, never}; use crate::{ db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, @@ -110,10 +110,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> T if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { return ty; } - let krate = owner.module(db.upcast()).krate(); - let trait_env = owner - .as_generic_def_id() - .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); + let trait_env = db.trait_environment_for_body(owner); let mut table = unify::InferenceTable::new(db, trait_env); let ty_with_vars = table.normalize_associated_types_in(ty); @@ -506,10 +503,7 @@ impl<'a> InferenceContext<'a> { body: &'a Body, resolver: Resolver, ) -> Self { - let krate = owner.module(db.upcast()).krate(); - let trait_env = owner - .as_generic_def_id() - .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); + let trait_env = db.trait_environment_for_body(owner); InferenceContext { result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env.clone()), @@ -851,7 +845,7 @@ impl<'a> InferenceContext<'a> { // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 let (resolution, unresolved) = if value_ns { - match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { Some(ResolveValueResult::ValueNs(value)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); @@ -872,11 +866,15 @@ impl<'a> InferenceContext<'a> { None => return (self.err_ty(), None), } } else { - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (self.err_ty(), None), } }; + let Some(mod_path) = path.mod_path() else { + never!("resolver should always resolve lang item paths"); + return (self.err_ty(), None); + }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = ctx.substs_from_path(path, strukt.into(), true); @@ -900,7 +898,7 @@ impl<'a> InferenceContext<'a> { let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let substs = generics.placeholder_subst(self.db); let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); - self.resolve_variant_on_alias(ty, unresolved, path) + self.resolve_variant_on_alias(ty, unresolved, mod_path) } TypeNs::TypeAliasId(it) => { let container = it.lookup(self.db.upcast()).container; @@ -917,7 +915,7 @@ impl<'a> InferenceContext<'a> { let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst) .fill_with_inference_vars(&mut self.table) .build(); - self.resolve_variant_on_alias(ty, unresolved, path) + self.resolve_variant_on_alias(ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { // FIXME this could happen in array size expressions, once we're checking them @@ -953,9 +951,9 @@ impl<'a> InferenceContext<'a> { &mut self, ty: Ty, unresolved: Option, - path: &Path, + path: &ModPath, ) -> (Ty, Option) { - let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0); + let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -969,7 +967,7 @@ impl<'a> InferenceContext<'a> { (ty, variant) } Some(1) => { - let segment = path.mod_path().segments().last().unwrap(); + let segment = path.segments().last().unwrap(); // this could be an enum variant or associated type if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { let enum_data = self.db.enum_data(enum_id); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index ee186673ee130..7bf227a27f28b 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -601,21 +601,21 @@ impl<'a> InferenceContext<'a> { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) } - Expr::Try { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); - if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) { - if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(inner_ty.clone()) - .build(); - self.write_method_resolution(tgt_expr, func, subst.clone()); - } - let try_output = self.resolve_output_on(trait_); - self.resolve_associated_type(inner_ty, try_output) - } else { - self.err_ty() - } - } + // Expr::Try { expr } => { + // let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); + // if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) { + // if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) { + // let subst = TyBuilder::subst_for_def(self.db, trait_, None) + // .push(inner_ty.clone()) + // .build(); + // self.write_method_resolution(tgt_expr, func, subst.clone()); + // } + // let try_output = self.resolve_output_on(trait_); + // self.resolve_associated_type(inner_ty, try_output) + // } else { + // self.err_ty() + // } + // } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); // FIXME: propagate the "castable to" expectation diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 2267fedaa8e92..266e410187581 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -39,7 +39,7 @@ impl<'a> InferenceContext<'a> { } else { // FIXME: report error, unresolved first path segment let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?; + self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; match value_or_partial { ResolveValueResult::ValueNs(it) => (it, None), diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 23b15087e3165..e7490087e762b 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -25,12 +25,12 @@ use hir_def::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, lang_item::{lang_attr, LangItem}, - path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments}, + path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, - AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, - HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId, - TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, + AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, + GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, + StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use intern::Interned; @@ -425,11 +425,10 @@ impl<'a> TyLoweringContext<'a> { if path.segments().len() > 1 { return None; } - let resolution = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { - Some((it, None)) => it, - _ => return None, - }; + let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { + Some((it, None)) => it, + _ => return None, + }; match resolution { TypeNs::GenericParam(param_id) => Some(param_id.into()), _ => None, @@ -608,7 +607,7 @@ impl<'a> TyLoweringContext<'a> { } let (resolution, remaining_index) = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), }; @@ -716,7 +715,7 @@ impl<'a> TyLoweringContext<'a> { resolved: ValueTyDefId, infer_args: bool, ) -> Substitution { - let last = path.segments().last().expect("path should have at least one segment"); + let last = path.segments().last(); let (segment, generic_def) = match resolved { ValueTyDefId::FunctionId(it) => (last, Some(it.into())), ValueTyDefId::StructId(it) => (last, Some(it.into())), @@ -732,13 +731,20 @@ impl<'a> TyLoweringContext<'a> { let len = path.segments().len(); let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx)); let segment = match penultimate { - Some(segment) if segment.args_and_bindings.is_some() => segment, + Some(segment) if segment.args_and_bindings.is_some() => Some(segment), _ => last, }; (segment, Some(var.parent.into())) } }; - self.substs_from_path_segment(segment, generic_def, infer_args, None) + if let Some(segment) = segment { + self.substs_from_path_segment(segment, generic_def, infer_args, None) + } else if let Some(generic_def) = generic_def { + // lang item + self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None) + } else { + Substitution::empty(Interner) + } } fn substs_from_path_segment( @@ -747,6 +753,21 @@ impl<'a> TyLoweringContext<'a> { def: Option, infer_args: bool, explicit_self_ty: Option, + ) -> Substitution { + self.substs_from_args_and_bindings( + segment.args_and_bindings, + def, + infer_args, + explicit_self_ty, + ) + } + + fn substs_from_args_and_bindings( + &self, + args_and_bindings: Option<&GenericArgs>, + def: Option, + infer_args: bool, + explicit_self_ty: Option, ) -> Substitution { // Remember that the item's own generic args come before its parent's. let mut substs = Vec::new(); @@ -780,7 +801,7 @@ impl<'a> TyLoweringContext<'a> { }; let mut had_explicit_args = false; - if let Some(generic_args) = &segment.args_and_bindings { + if let Some(generic_args) = &args_and_bindings { if !generic_args.has_self_type { fill_self_params(); } @@ -879,12 +900,11 @@ impl<'a> TyLoweringContext<'a> { path: &Path, explicit_self_ty: Option, ) -> Option { - let resolved = - match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? { - // FIXME(trait_alias): We need to handle trait alias here. - TypeNs::TraitId(tr) => tr, - _ => return None, - }; + let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { + // FIXME(trait_alias): We need to handle trait alias here. + TypeNs::TraitId(tr) => tr, + _ => return None, + }; let segment = path.segments().last().expect("path should have at least one segment"); Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } @@ -1381,9 +1401,7 @@ pub(crate) fn generic_predicates_for_param_query( Some(it) => it, None => return true, }; - let tr = match resolver - .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()) - { + let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) { Some(TypeNs::TraitId(tr)) => tr, _ => return false, }; @@ -1423,6 +1441,17 @@ pub(crate) fn generic_predicates_for_param_recover( Arc::new([]) } +pub(crate) fn trait_environment_for_body_query( + db: &dyn HirDatabase, + def: DefWithBodyId, +) -> Arc { + let Some(def) = def.as_generic_def_id() else { + let krate = def.module(db.upcast()).krate(); + return Arc::new(TraitEnvironment::empty(krate)); + }; + db.trait_environment(def) +} + pub(crate) fn trait_environment_query( db: &dyn HirDatabase, def: GenericDefId, @@ -1948,7 +1977,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( // as types. Maybe here is not the best place to do it, but // it works. if let TypeRef::Path(p) = t { - let p = p.mod_path(); + let p = p.mod_path()?; if p.kind == PathKind::Plain { if let [n] = p.segments() { let c = ConstRefOrPath::Path(n.clone()); @@ -1977,8 +2006,15 @@ pub(crate) fn const_or_path_to_chalk( ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()), ConstRefOrPath::Path(n) => { let path = ModPath::from_segments(PathKind::Plain, Some(n.clone())); - path_to_const(db, resolver, &path, mode, args, debruijn) - .unwrap_or_else(|| unknown_const(expected_ty)) + path_to_const( + db, + resolver, + &Path::from_known_path_with_no_generic(path), + mode, + args, + debruijn, + ) + .unwrap_or_else(|| unknown_const(expected_ty)) } } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index c5d843d9ebd89..1ff6e106021f2 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,6 +1,6 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, iter}; +use std::{borrow::Cow, collections::HashMap, iter, sync::Arc}; use base_db::CrateId; use chalk_ir::{ @@ -24,7 +24,8 @@ use crate::{ layout::layout_of_ty, mapping::from_chalk, method_resolution::lookup_impl_method, - CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt, + CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, + TyBuilder, TyExt, }; use super::{ @@ -34,6 +35,7 @@ use super::{ pub struct Evaluator<'a> { db: &'a dyn HirDatabase, + trait_env: Arc, stack: Vec, heap: Vec, crate_id: CrateId, @@ -217,8 +219,7 @@ pub fn interpret_mir( assert_placeholder_ty_is_unused: bool, ) -> Result { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = - Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused); + let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); let bytes = evaluator.interpret_mir_with_no_arg(&body)?; let memory_map = evaluator.create_memory_map( &bytes, @@ -231,13 +232,16 @@ pub fn interpret_mir( impl Evaluator<'_> { pub fn new<'a>( db: &'a dyn HirDatabase, - crate_id: CrateId, + body: &MirBody, assert_placeholder_ty_is_unused: bool, ) -> Evaluator<'a> { + let crate_id = body.owner.module(db.upcast()).krate(); + let trait_env = db.trait_environment_for_body(body.owner); Evaluator { stack: vec![0], heap: vec![0], db, + trait_env, crate_id, assert_placeholder_ty_is_unused, stack_depth_limit: 100, @@ -500,15 +504,9 @@ impl Evaluator<'_> { } 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, + self.trait_env.clone(), def, generic_args.clone(), ); @@ -584,7 +582,7 @@ impl Evaluator<'_> { .to_owned()); } Terminator::Unreachable => { - return Err(MirEvalError::UndefinedBehavior("unreachable executed")) + return Err(MirEvalError::UndefinedBehavior("unreachable executed")); } _ => not_supported!("unknown terminator"), } @@ -710,8 +708,24 @@ impl Evaluator<'_> { let ty = self.place_ty(p, locals)?; let bytes = self.eval_place(p, locals)?.get(&self)?; let layout = self.layout(&ty)?; + let enum_id = 'b: { + match ty.kind(Interner) { + TyKind::Adt(e, _) => match e.0 { + AdtId::EnumId(e) => break 'b e, + _ => (), + }, + _ => (), + } + return Ok(Owned(0u128.to_le_bytes().to_vec())); + }; match layout.variants { - Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()), + Variants::Single { index } => { + let r = self.db.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: index.0, + })?; + Owned(r.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"); @@ -727,13 +741,6 @@ impl Evaluator<'_> { 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() { @@ -790,8 +797,8 @@ impl Evaluator<'_> { Owned(result) } AggregateKind::Adt(x, subst) => { - let (size, variant_layout, tag) = - self.layout_of_variant(*x, subst.clone(), locals)?; + let subst = self.subst_filler(subst, locals); + let (size, variant_layout, tag) = self.layout_of_variant(*x, subst, locals)?; Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?) } }, @@ -1124,12 +1131,13 @@ impl Evaluator<'_> { } fn detect_lang_function(&self, def: FunctionId) -> Option { + use LangItem::*; let candidate = lang_attr(self.db.upcast(), def)?; - // filter normal lang functions out - if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) { - return None; + // We want to execute these functions with special logic + if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) { + return Some(candidate); } - Some(candidate) + None } fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index c4dd7c0ace46c..3b9a31c772a5b 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -13,7 +13,7 @@ use hir_def::{ layout::LayoutError, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, EnumVariantId, HasModule, + DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, TraitId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -50,6 +50,8 @@ pub enum MirLowerError { ConstEvalError(Box), LayoutError(LayoutError), IncompleteExpr, + /// Trying to lower a trait function, instead of an implementation + TraitFunctionDefinition(TraitId, Name), UnresolvedName(String), RecordLiteralWithoutPath, UnresolvedMethod, @@ -200,12 +202,21 @@ impl MirLowerCtx<'_> { mut current: BasicBlockId, ) -> Result> { match &self.body.exprs[expr_id] { - Expr::Missing => Err(MirLowerError::IncompleteExpr), + Expr::Missing => { + if let DefWithBodyId::FunctionId(f) = self.owner { + let assoc = self.db.lookup_intern_function(f); + if let ItemContainerId::TraitId(t) = assoc.container { + let name = &self.db.function_data(f).name; + return Err(MirLowerError::TraitFunctionDefinition(t, name.clone())); + } + } + 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()) + .resolve_path_in_value_ns(self.db.upcast(), p) .ok_or_else(unresolved_name)?; let pr = match pr { ResolveValueResult::ValueNs(v) => v, @@ -608,7 +619,6 @@ impl MirLowerCtx<'_> { } } 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"), diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index fe8147dcd3e7a..b683dd7f90d62 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -125,7 +125,7 @@ impl MirLowerCtx<'_> { 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 { + let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) else { return Err(MirLowerError::unresolved_path(self.db, p)); }; let pr = match pr { diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index ffc08b7e346c7..70364d0882314 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -77,6 +77,7 @@ impl Display for LocalName { impl<'a> MirPrettyCtx<'a> { fn for_body(&mut self) { + wln!(self, "// {:?}", self.body.owner); self.with_block(|this| { this.locals(); wln!(this); diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 34d957e26ef5b..a6967414aa855 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -130,7 +130,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra WherePredicate::Lifetime { .. } => None, }) .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None)) - .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { + .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) { Some(TypeNs::TraitId(t)) => Some(t), _ => None, }) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 407ba6f65844e..9709970db1eec 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1076,10 +1076,7 @@ impl<'db> SemanticsImpl<'db> { let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id); let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; - match analyze - .resolver - .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())? - { + match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? { TypeNs::TraitId(id) => Some(Trait { id }), _ => None, } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index c24d196e1b624..5b18e445727f3 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -420,7 +420,10 @@ impl SourceAnalyzer { None } else { // Shorthand syntax, resolve to the local - let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); + let path = Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + once(local_name.clone()), + )); match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { Some(ValueNs::LocalBinding(binding_id)) => { Some(Local { binding_id, parent: self.resolver.body_owner()? }) @@ -461,7 +464,7 @@ impl SourceAnalyzer { ) -> Option { let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id); let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; - self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) + self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into()) } pub(crate) fn resolve_bind_pat_to_const( @@ -801,15 +804,11 @@ impl SourceAnalyzer { func: FunctionId, substs: Substitution, ) -> FunctionId { - let krate = self.resolver.krate(); let owner = match self.resolver.body_owner() { Some(it) => it, None => return func, }; - let env = owner.as_generic_def_id().map_or_else( - || Arc::new(hir_ty::TraitEnvironment::empty(krate)), - |d| db.trait_environment(d), - ); + let env = db.trait_environment_for_body(owner); method_resolution::lookup_impl_method(db, env, func, substs).0 } @@ -819,15 +818,11 @@ impl SourceAnalyzer { const_id: ConstId, subs: Substitution, ) -> ConstId { - let krate = self.resolver.krate(); let owner = match self.resolver.body_owner() { Some(it) => it, None => return const_id, }; - let env = owner.as_generic_def_id().map_or_else( - || Arc::new(hir_ty::TraitEnvironment::empty(krate)), - |d| db.trait_environment(d), - ); + let env = db.trait_environment_for_body(owner); method_resolution::lookup_impl_const(db, env, const_id, subs).0 } @@ -946,7 +941,7 @@ pub(crate) fn resolve_hir_path_as_macro( resolver: &Resolver, path: &Path, ) -> Option { - resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into) + resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(Into::into) } fn resolve_hir_path_( @@ -962,8 +957,7 @@ fn resolve_hir_path_( res.map(|ty_ns| (ty_ns, path.segments().first())) } None => { - let (ty, remaining_idx) = - resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?; + let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { @@ -1019,7 +1013,7 @@ fn resolve_hir_path_( let body_owner = resolver.body_owner(); let values = || { - resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { + resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| { let res = match val { ValueNs::LocalBinding(binding_id) => { let var = Local { parent: body_owner?, binding_id }; @@ -1039,14 +1033,14 @@ fn resolve_hir_path_( let items = || { resolver - .resolve_module_path_in_items(db.upcast(), path.mod_path()) + .resolve_module_path_in_items(db.upcast(), path.mod_path()?) .take_types() .map(|it| PathResolution::Def(it.into())) }; let macros = || { resolver - .resolve_path_as_macro(db.upcast(), path.mod_path()) + .resolve_path_as_macro(db.upcast(), path.mod_path()?) .map(|def| PathResolution::Def(ModuleDef::Macro(def.into()))) }; @@ -1074,7 +1068,7 @@ fn resolve_hir_path_qualifier( path: &Path, ) -> Option { resolver - .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()) + .resolve_path_in_type_ns_fully(db.upcast(), &path) .map(|ty| match ty { TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()), @@ -1089,7 +1083,7 @@ fn resolve_hir_path_qualifier( }) .or_else(|| { resolver - .resolve_module_path_in_items(db.upcast(), path.mod_path()) + .resolve_module_path_in_items(db.upcast(), path.mod_path()?) .take_types() .map(|it| PathResolution::Def(it.into())) }) diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs index 38fccb33829a3..2e26f59d030ff 100644 --- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs @@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists}; // Replaces a `try` expression with a `match` expression. // // ``` -// # //- minicore:option +// # //- minicore: try, option // fn handle() { // let pat = Some(true)$0?; // } @@ -111,7 +111,7 @@ mod tests { check_assist( replace_try_expr_with_match, r#" -//- minicore:option +//- minicore: try, option fn test() { let pat = Some(true)$0?; } @@ -132,7 +132,7 @@ fn test() { check_assist( replace_try_expr_with_match, r#" -//- minicore:result +//- minicore: try, from, result fn test() { let pat = Ok(true)$0?; } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index e5a8d675a9ead..aff11367de9a6 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -2352,7 +2352,7 @@ fn doctest_replace_try_expr_with_match() { check_doc_test( "replace_try_expr_with_match", r#####" -//- minicore:option +//- minicore: try, option fn handle() { let pat = Some(true)$0?; } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 57bf0f9ad5f3c..70ec915e96745 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -5009,7 +5009,7 @@ fn foo() { fn hover_try_expr_res() { check_hover_range( r#" -//- minicore:result +//- minicore: try, from, result struct FooError; fn foo() -> Result<(), FooError> { @@ -5023,7 +5023,7 @@ fn foo() -> Result<(), FooError> { ); check_hover_range( r#" -//- minicore:result +//- minicore: try, from, result struct FooError; struct BarError; @@ -5044,6 +5044,7 @@ fn foo() -> Result<(), FooError> { fn hover_try_expr() { check_hover_range( r#" +//- minicore: try struct NotResult(T, U); struct Short; struct Looooong; @@ -5061,6 +5062,7 @@ fn foo() -> NotResult<(), Looooong> { ); check_hover_range( r#" +//- minicore: try struct NotResult(T, U); struct Short; struct Looooong; @@ -5092,7 +5094,7 @@ fn foo() -> Option<()> { "#, expect![[r#" ```rust - as Try>::Output + i32 ```"#]], ); } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index ca6de4061a4b8..8dd9f306c8a62 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -181,7 +181,7 @@ pub mod convert { } // endregion:as_ref // region:infallible - pub enum Infallibe {} + pub enum Infallible {} // endregion:infallible } @@ -380,11 +380,15 @@ pub mod ops { // endregion:fn // region:try mod try_ { + use super::super::convert::Infallible; + pub enum ControlFlow { + #[lang = "Continue"] Continue(C), + #[lang = "Break"] Break(B), } - pub trait FromResidual { + pub trait FromResidual::Residual> { #[lang = "from_residual"] fn from_residual(residual: R) -> Self; } @@ -400,14 +404,66 @@ pub mod ops { impl Try for ControlFlow { type Output = C; - type Residual = ControlFlow; + type Residual = ControlFlow; fn from_output(output: Self::Output) -> Self {} fn branch(self) -> ControlFlow {} } impl FromResidual for ControlFlow { - fn from_residual(residual: ControlFlow) -> Self {} + fn from_residual(residual: ControlFlow) -> Self {} + } + // region:option + impl Try for Option { + type Output = T; + type Residual = Option; + fn from_output(output: Self::Output) -> Self { + Some(output) + } + fn branch(self) -> ControlFlow { + match self { + Some(x) => ControlFlow::Continue(x), + None => ControlFlow::Break(None), + } + } + } + + impl FromResidual for Option { + fn from_residual(x: Option) -> Self { + match x { + None => None, + } + } + } + // endregion:option + // region:result + // region:from + use super::super::convert::From; + + impl Try for Result { + type Output = T; + type Residual = Result; + + fn from_output(output: Self::Output) -> Self { + Ok(output) + } + + fn branch(self) -> ControlFlow { + match self { + Ok(v) => ControlFlow::Continue(v), + Err(e) => ControlFlow::Break(Err(e)), + } + } + } + + impl> FromResidual> for Result { + fn from_residual(residual: Result) -> Self { + match residual { + Err(e) => Err(From::from(e)), + } + } } + // endregion:from + // endregion:result } pub use self::try_::{ControlFlow, FromResidual, Try}; // endregion:try From 1b85b43e6f5283963e901597c67e89b82d90df01 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 8 Mar 2023 23:09:58 +0330 Subject: [PATCH 008/806] add mir-stats to analysis-stats --- .../rust-analyzer/src/cli/analysis_stats.rs | 23 +++++++++++++++++++ crates/rust-analyzer/src/cli/flags.rs | 3 +++ 2 files changed, 26 insertions(+) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 6ce1de5d32bca..e1504743bfdb6 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,6 +2,7 @@ //! errors. use std::{ + collections::HashMap, env, time::{SystemTime, UNIX_EPOCH}, }; @@ -153,6 +154,10 @@ impl flags::AnalysisStats { self.run_inference(&host, db, &vfs, &funcs, verbosity); } + if self.mir_stats { + self.lower_mir(db, &funcs); + } + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -189,6 +194,24 @@ impl flags::AnalysisStats { Ok(()) } + fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) { + let all = funcs.len(); + let mut fail = 0; + let mut h: HashMap = HashMap::new(); + for f in funcs { + let f = FunctionId::from(*f); + let Err(e) = db.mir_body(f.into()) else { + continue; + }; + let es = format!("{:?}", e); + *h.entry(es).or_default() += 1; + fail += 1; + } + let h = h.into_iter().sorted_by_key(|x| x.1).collect::>(); + eprintln!("Mir failed reasons: {:#?}", h); + eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); + } + fn run_inference( &self, host: &AnalysisHost, diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 770612cc9478d..b085a0a892a50 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -66,6 +66,8 @@ xflags::xflags! { optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats + /// Print the number of bodies that fail to lower to mir, in addition to failed reasons. + optional --mir-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -172,6 +174,7 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, + pub mir_stats: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool, From a063f000ff99989406abd1e6f58a9c2b576ba41a Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 11 Mar 2023 21:43:53 +0330 Subject: [PATCH 009/806] Support function pointer MIR lowering --- crates/hir-expand/src/name.rs | 2 + crates/hir-ty/src/consteval/tests.rs | 102 ++++++ crates/hir-ty/src/infer.rs | 13 +- crates/hir-ty/src/infer/coerce.rs | 17 +- crates/hir-ty/src/infer/expr.rs | 35 ++- crates/hir-ty/src/infer/unify.rs | 36 ++- crates/hir-ty/src/lib.rs | 6 +- crates/hir-ty/src/method_resolution.rs | 8 +- crates/hir-ty/src/mir/eval.rs | 297 ++++++++++++------ crates/hir-ty/src/mir/lower.rs | 46 +-- crates/hir-ty/src/mir/pretty.rs | 4 +- crates/hir-ty/src/tests/coercion.rs | 30 ++ crates/hir-ty/src/traits.rs | 9 + .../src/handlers/mutability_errors.rs | 25 ++ 14 files changed, 467 insertions(+), 163 deletions(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index c3462beac73c5..71eb35d9df8e4 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -343,6 +343,8 @@ pub mod known { feature, // known methods of lang items call_once, + call_mut, + call, eq, ne, ge, diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 3bec2ee88bfa3..8a9a5d254df84 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -906,6 +906,108 @@ fn or_pattern() { ); } +#[test] +fn function_pointer() { + check_number( + r#" + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = { + let plus2 = add2; + plus2(3) + }; + "#, + 5, + ); + check_number( + r#" + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = { + let plus2: fn(u8) -> u8 = add2; + plus2(3) + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + fn add2(x: u8) -> u8 { + x + 2 + } + fn mult3(x: u8) -> u8 { + x * 3 + } + const GOAL: u8 = { + let x = [add2, mult3]; + x[0](1) + x[1](5) + }; + "#, + 18, + ); +} + +#[test] +fn function_traits() { + check_number( + r#" + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3); + "#, + 15, + ); + check_number( + r#" + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = { + let add2: fn(u8) -> u8 = add2; + call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3) + }; + "#, + 15, + ); + check_number( + r#" + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(&&&&&add2, 3); + "#, + 5, + ); +} + #[test] fn array_and_index() { check_number( diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 00b5f7948ac4c..06d74215fffbf 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -38,9 +38,9 @@ use stdx::{always, never}; use crate::{ db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal, - GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const, + DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, + Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; // This lint has a false positive here. See the link below for details. @@ -273,6 +273,13 @@ pub struct Adjustment { pub target: Ty, } +impl Adjustment { + pub fn borrow(m: Mutability, ty: Ty) -> Self { + let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Adjust { /// Go from ! to any type. diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 48c91530266df..6e899249b6969 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -51,11 +51,12 @@ fn success( pub(super) struct CoerceMany { expected_ty: Ty, final_ty: Option, + expressions: Vec, } impl CoerceMany { pub(super) fn new(expected: Ty) -> Self { - CoerceMany { expected_ty: expected, final_ty: None } + CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] } } /// Returns the "expected type" with which this coercion was @@ -125,8 +126,15 @@ impl CoerceMany { let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty); let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty); if let (Ok(result1), Ok(result2)) = (result1, result2) { - ctx.table.register_infer_ok(result1); - ctx.table.register_infer_ok(result2); + ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals }); + for &e in &self.expressions { + ctx.write_expr_adj(e, result1.value.0.clone()); + } + ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals }); + if let Some(expr) = expr { + ctx.write_expr_adj(expr, result2.value.0); + self.expressions.push(expr); + } return self.final_ty = Some(target_ty); } } @@ -148,6 +156,9 @@ impl CoerceMany { } cov_mark::hit!(coerce_merge_fail_fallback); } + if let Some(expr) = expr { + self.expressions.push(expr); + } } } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 7bf227a27f28b..ca285f2fec0ae 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -34,6 +34,7 @@ use crate::{ method_resolution::{self, lang_items_for_bin_op, VisibleFromModule}, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, + traits::FnTrait, utils::{generics, Generics}, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, @@ -385,16 +386,32 @@ impl<'a> InferenceContext<'a> { || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let adjustments = auto_deref_adjust_steps(&derefs); - // FIXME: Handle call adjustments for Fn/FnMut - self.write_expr_adj(*callee, adjustments); - if let Some((trait_, func)) = func { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(callee_ty.clone()) - .push(TyBuilder::tuple_with(params.iter().cloned())) - .build(); - self.write_method_resolution(tgt_expr, func, subst.clone()); + let mut adjustments = auto_deref_adjust_steps(&derefs); + if let Some(fn_x) = func { + match fn_x { + FnTrait::FnOnce => (), + FnTrait::FnMut => adjustments.push(Adjustment::borrow( + Mutability::Mut, + derefed_callee.clone(), + )), + FnTrait::Fn => adjustments.push(Adjustment::borrow( + Mutability::Not, + derefed_callee.clone(), + )), + } + let trait_ = fn_x + .get_id(self.db, self.trait_env.krate) + .expect("We just used it"); + let trait_data = self.db.trait_data(trait_); + if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) { + let subst = TyBuilder::subst_for_def(self.db, trait_, None) + .push(callee_ty.clone()) + .push(TyBuilder::tuple_with(params.iter().cloned())) + .build(); + self.write_method_resolution(tgt_expr, func, subst.clone()); + } } + self.write_expr_adj(*callee, adjustments); (params, ret_ty) } None => { diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 504f0743aa947..2a07dd708c2a5 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -8,16 +8,15 @@ use chalk_ir::{ }; use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; -use hir_def::{FunctionId, TraitId}; use hir_expand::name; use stdx::never; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar, - Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, + AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, + InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, + Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -631,7 +630,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option<(TraitId, FunctionId)>, Vec, Ty)> { + ) -> Option<(Option, Vec, Ty)> { match ty.callable_sig(self.db) { Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), None => self.callable_sig_from_fn_trait(ty, num_args), @@ -642,7 +641,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option<(TraitId, FunctionId)>, Vec, Ty)> { + ) -> Option<(Option, Vec, Ty)> { let krate = self.trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let trait_data = self.db.trait_data(fn_once_trait); @@ -676,19 +675,28 @@ impl<'a> InferenceTable<'a> { }; let trait_env = self.trait_env.env.clone(); + let mut trait_ref = projection.trait_ref(self.db); let obligation = InEnvironment { - goal: projection.trait_ref(self.db).cast(Interner), - environment: trait_env, + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); - Some(( - Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))), - arg_tys, - return_ty, - )) + for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + let fn_x_trait = fn_x.get_id(self.db, krate)?; + trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); + let obligation: chalk_ir::InEnvironment> = InEnvironment { + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), + }; + let canonical = self.canonicalize(obligation.clone()); + if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { + return Some((Some(fn_x), arg_tys, return_ty)); + } + } + unreachable!("It should at least implement FnOnce at this point"); } else { None } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 9c63d67ab19a4..782a8ab4aa2ab 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -576,10 +576,14 @@ where } pub fn callable_sig_from_fnonce( - self_ty: &Ty, + mut self_ty: &Ty, env: Arc, db: &dyn HirDatabase, ) -> Option { + if let Some((ty, _, _)) = self_ty.as_reference() { + // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment + self_ty = ty; + } let krate = env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index f3a27632bf545..f105c94086c9b 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -20,7 +20,7 @@ use crate::{ autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, - infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, + infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, @@ -600,9 +600,9 @@ impl ReceiverAdjustments { } } if let Some(m) = self.autoref { - ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); - adjust - .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() }); + let a = Adjustment::borrow(m, ty); + ty = a.target.clone(); + adjust.push(a); } if self.unsize_array { ty = 'x: { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 1ff6e106021f2..88ef92a4ae6c8 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -11,7 +11,7 @@ use hir_def::{ builtin_type::BuiltinType, lang_item::{lang_attr, LangItem}, layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId, }; use intern::Interned; use la_arena::ArenaMap; @@ -24,8 +24,9 @@ use crate::{ layout::layout_of_ty, mapping::from_chalk, method_resolution::lookup_impl_method, - CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, - TyBuilder, TyExt, + traits::FnTrait, + CallableDefId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; use super::{ @@ -33,11 +34,37 @@ use super::{ Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp, }; +#[derive(Debug, Default)] +struct VTableMap { + ty_to_id: HashMap, + id_to_ty: Vec, +} + +impl VTableMap { + fn id(&mut self, ty: Ty) -> usize { + if let Some(x) = self.ty_to_id.get(&ty) { + return *x; + } + let id = self.id_to_ty.len(); + self.id_to_ty.push(ty.clone()); + self.ty_to_id.insert(ty, id); + id + } + + fn ty(&self, id: usize) -> Result<&Ty> { + self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id)) + } +} + pub struct Evaluator<'a> { db: &'a dyn HirDatabase, trait_env: Arc, stack: Vec, heap: Vec, + /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we + /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the + /// time of use. + vtable_map: VTableMap, crate_id: CrateId, // FIXME: This is a workaround, see the comment on `interpret_mir` assert_placeholder_ty_is_unused: bool, @@ -147,6 +174,7 @@ pub enum MirEvalError { ExecutionLimitExceeded, StackOverflow, TargetDataLayoutNotAvailable, + InvalidVTableId(usize), } impl std::fmt::Debug for MirEvalError { @@ -168,6 +196,7 @@ impl std::fmt::Debug for MirEvalError { Self::MirLowerError(arg0, arg1) => { f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() } + Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), Self::InvalidConst(arg0) => { let data = &arg0.data(Interner); @@ -240,6 +269,7 @@ impl Evaluator<'_> { Evaluator { stack: vec![0], heap: vec![0], + vtable_map: VTableMap::default(), db, trait_env, crate_id, @@ -461,108 +491,16 @@ impl Evaluator<'_> { } => { let fn_ty = self.operand_ty(func, &locals)?; match &fn_ty.data(Interner).kind { + TyKind::Function(_) => { + let bytes = self.eval_operand(func, &locals)?; + self.exec_fn_pointer(bytes, destination, args, &locals)?; + } 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 (imp, generic_args) = lookup_impl_method( - self.db, - self.trait_env.clone(), - 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"); + self.exec_fn_def(*def, generic_args, destination, args, &locals)?; } - _ => not_supported!("unknown function type"), + x => not_supported!("unknown function type {x:?}"), } + current_block_idx = target.expect("broken mir, function without target"); } Terminator::SwitchInt { discr, targets } => { let val = u128::from_le_bytes(pad16( @@ -808,6 +746,16 @@ impl Evaluator<'_> { not_supported!("creating pointer from exposed address") } CastKind::Pointer(cast) => match cast { + PointerCast::ReifyFnPointer => { + let current_ty = self.operand_ty(operand, locals)?; + if let TyKind::FnDef(_, _) = ¤t_ty.data(Interner).kind { + let id = self.vtable_map.id(current_ty); + let ptr_size = self.ptr_size(); + Owned(id.to_le_bytes()[0..ptr_size].to_vec()) + } else { + not_supported!("ReifyFnPointer cast of a non FnDef type"); + } + } PointerCast::Unsize => { let current_ty = self.operand_ty(operand, locals)?; match &target_ty.data(Interner).kind { @@ -920,7 +868,7 @@ impl Evaluator<'_> { size: usize, // Not neccessarily equal to variant_layout.size variant_layout: &Layout, tag: Option<(usize, usize, i128)>, - values: &Vec, + values: &[Operand], locals: &Locals<'_>, ) -> Result> { let mut result = vec![0; size]; @@ -1140,6 +1088,20 @@ impl Evaluator<'_> { None } + fn detect_fn_trait(&self, def: FunctionId) -> Option { + use LangItem::*; + let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { + return None; + }; + let l = lang_attr(self.db.upcast(), parent)?; + match l { + FnOnce => Some(FnTrait::FnOnce), + FnMut => Some(FnTrait::FnMut), + Fn => Some(FnTrait::Fn), + _ => None, + } + } + fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { // FIXME: support indirect references let mut mm = MemoryMap::default(); @@ -1228,7 +1190,134 @@ impl Evaluator<'_> { } } - pub(crate) fn exec_lang_item( + fn exec_fn_pointer( + &mut self, + bytes: Interval, + destination: &Place, + args: &[Operand], + locals: &Locals<'_>, + ) -> Result<()> { + let id = from_bytes!(usize, bytes.get(self)?); + let next_ty = self.vtable_map.ty(id)?.clone(); + if let TyKind::FnDef(def, generic_args) = &next_ty.data(Interner).kind { + self.exec_fn_def(*def, generic_args, destination, args, &locals)?; + } else { + return Err(MirEvalError::TypeError("function pointer to non function")); + } + Ok(()) + } + + fn exec_fn_def( + &mut self, + def: FnDefId, + generic_args: &Substitution, + destination: &Place, + args: &[Operand], + locals: &Locals<'_>, + ) -> Result<()> { + let def: CallableDefId = from_chalk(self.db, def); + let generic_args = self.subst_filler(generic_args, &locals); + match def { + CallableDefId::FunctionId(def) => { + let dest_addr = self.place_addr(destination, &locals)?; + if let Some(x) = self.detect_fn_trait(def) { + self.exec_fn_trait(x, &args, destination, locals)?; + return Ok(()); + } + 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 (imp, generic_args) = lookup_impl_method( + self.db, + self.trait_env.clone(), + 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)))? + }; + 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)?; + } + } + Ok(()) + } + + fn exec_fn_trait( + &mut self, + ft: FnTrait, + args: &[Operand], + destination: &Place, + locals: &Locals<'_>, + ) -> Result<()> { + let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; + let ref_func_ty = self.operand_ty(func, locals)?; + let func_ty = match ft { + FnTrait::FnOnce => ref_func_ty, + FnTrait::FnMut | FnTrait::Fn => match ref_func_ty.as_reference() { + Some(x) => x.0.clone(), + None => return Err(MirEvalError::TypeError("fn trait with non-reference arg")), + }, + }; + match &func_ty.data(Interner).kind { + TyKind::FnDef(def, subst) => { + self.exec_fn_def(*def, subst, destination, &args[1..], locals)?; + } + TyKind::Function(_) => { + let mut func_data = self.eval_operand(func, locals)?; + if let FnTrait::FnMut | FnTrait::Fn = ft { + let addr = Address::from_bytes(func_data.get(self)?)?; + func_data = Interval { addr, size: self.ptr_size() }; + } + self.exec_fn_pointer(func_data, destination, &args[1..], locals)?; + } + x => not_supported!("Call {ft:?} trait methods with type {x:?}"), + } + Ok(()) + } + + fn exec_lang_item( &self, x: LangItem, mut args: std::vec::IntoIter>, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 3b9a31c772a5b..7a5ca0894200c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -298,7 +298,7 @@ impl MirLowerCtx<'_> { ); Ok(Some(current)) } - ValueNs::StructId(_) => { + ValueNs::FunctionId(_) | ValueNs::StructId(_) => { // It's probably a unit struct or a zero sized function, so no action is needed. Ok(Some(current)) } @@ -445,36 +445,36 @@ impl MirLowerCtx<'_> { }) }, Expr::Call { callee, args, .. } => { + if let Some((func_id, generic_args)) = + self.infer.method_resolution(expr_id) { + 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); + return self.lower_call_and_args( + func, + iter::once(*callee).chain(args.iter().copied()), + place, + current, + self.is_uninhabited(expr_id), + ); + } 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")) + chalk_ir::TyKind::Function(_) => { + let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { + return Ok(None); + }; + self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) } 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"), + _ => return Err(MirLowerError::TypeError("function call on bad type")), } } Expr::MethodCall { receiver, args, .. } => { diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 70364d0882314..eb0002266dc56 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -301,9 +301,9 @@ impl<'a> MirPrettyCtx<'a> { w!(self, ")"); } Rvalue::Cast(ck, op, ty) => { - w!(self, "Discriminant({ck:?}"); + w!(self, "Cast({ck:?}, "); self.operand(op); - w!(self, "{})", ty.display(self.db)); + w!(self, ", {})", ty.display(self.db)); } Rvalue::CheckedBinaryOp(b, o1, o2) => { self.operand(o1); diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index b524922b6cf40..696bdef03fcba 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -396,10 +396,40 @@ fn test() { ); } +#[test] +fn coerce_fn_item_to_fn_ptr_in_array() { + check_no_mismatches( + r" +fn foo(x: u32) -> isize { 1 } +fn bar(x: u32) -> isize { 1 } +fn test() { + let f = [foo, bar]; + // ^^^ adjustments: Pointer(ReifyFnPointer) +}", + ); +} + #[test] fn coerce_fn_items_in_match_arms() { cov_mark::check!(coerce_fn_reification); + check_no_mismatches( + r" +fn foo1(x: u32) -> isize { 1 } +fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + 2 => foo2, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + _ => foo3, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + }; + x; +}", + ); check_types( r" fn foo1(x: u32) -> isize { 1 } diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 3ab85c68f5b9d..aebf59f31525a 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -11,6 +11,7 @@ use hir_def::{ lang_item::{LangItem, LangItemTarget}, TraitId, }; +use hir_expand::name::{name, Name}; use stdx::panic_context; use crate::{ @@ -187,6 +188,14 @@ impl FnTrait { } } + pub fn method_name(&self) -> Name { + match self { + FnTrait::FnOnce => name!(call_once), + FnTrait::FnMut => name!(call_mut), + FnTrait::Fn => name!(call), + } + } + pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option { let target = db.lang_item(krate, self.lang_item())?; match target { diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 96470265d11d1..03951ea2bf51b 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -631,6 +631,31 @@ fn f(inp: (Foo, Foo, Foo, Foo)) { ); } + #[test] + fn fn_traits() { + check_diagnostics( + r#" +//- minicore: fn +fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { + x(2) + //^ 💡 error: cannot mutate immutable variable `x` +} +fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +"#, + ); + } + #[test] fn respect_allow_unused_mut() { // FIXME: respect From 7525a38af5ebd9eef404b19a11cefe8a033f9d2d Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 14 Mar 2023 12:14:02 +0330 Subject: [PATCH 010/806] Support evaluating `dyn Trait` methods --- crates/hir-ty/src/consteval/tests.rs | 51 ++++++++ crates/hir-ty/src/method_resolution.rs | 39 +++++- crates/hir-ty/src/mir/eval.rs | 158 ++++++++++++++++--------- crates/hir-ty/src/mir/lower.rs | 9 +- 4 files changed, 197 insertions(+), 60 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 8a9a5d254df84..f7914b578e437 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1008,6 +1008,57 @@ fn function_traits() { ); } +#[test] +fn dyn_trait() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + trait Foo { + fn foo(&self) -> u8 { 10 } + } + struct S1; + struct S2; + struct S3; + impl Foo for S1 { + fn foo(&self) -> u8 { 1 } + } + impl Foo for S2 { + fn foo(&self) -> u8 { 2 } + } + impl Foo for S3 {} + const GOAL: u8 = { + let x: &[&dyn Foo] = &[&S1, &S2, &S3]; + x[0].foo() + x[1].foo() + x[2].foo() + }; + "#, + 13, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + trait Foo { + fn foo(&self) -> i32 { 10 } + } + trait Bar { + fn bar(&self) -> i32 { 20 } + } + + struct S; + impl Foo for S { + fn foo(&self) -> i32 { 200 } + } + impl Bar for dyn Foo { + fn bar(&self) -> i32 { 700 } + } + const GOAL: i32 = { + let x: &dyn Foo = &S; + x.bar() + x.foo() + }; + "#, + 900, + ); +} + #[test] fn array_and_index() { check_number( diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index f105c94086c9b..6244b98104fe6 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -5,7 +5,7 @@ use std::{ops::ControlFlow, sync::Arc}; use base_db::{CrateId, Edition}; -use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex}; +use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, @@ -692,6 +692,38 @@ pub fn lookup_impl_const( .unwrap_or((const_id, subs)) } +/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should +/// call the method using the vtable. +pub fn is_dyn_method( + db: &dyn HirDatabase, + _env: Arc, + func: FunctionId, + fn_subst: Substitution, +) -> Option { + let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { + return None; + }; + let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let fn_params = fn_subst.len(Interner) - trait_params; + let trait_ref = TraitRef { + trait_id: to_chalk_trait_id(trait_id), + substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)), + }; + let self_ty = trait_ref.self_type_parameter(Interner); + if let TyKind::Dyn(d) = self_ty.kind(Interner) { + let is_my_trait_in_bounds = d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { + // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter + // what the generics are, we are sure that the method is come from the vtable. + WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, + _ => false, + }); + if is_my_trait_in_bounds { + return Some(fn_params); + } + } + None +} + /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. @@ -701,9 +733,8 @@ pub fn lookup_impl_method( func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution) { - let trait_id = match func.lookup(db.upcast()).container { - ItemContainerId::TraitId(id) => id, - _ => return (func, fn_subst), + let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { + return (func, fn_subst) }; let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); let fn_params = fn_subst.len(Interner) - trait_params; diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 88ef92a4ae6c8..7293156a978f9 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -23,10 +23,10 @@ use crate::{ infer::{normalize, PointerCast}, layout::layout_of_ty, mapping::from_chalk, - method_resolution::lookup_impl_method, + method_resolution::{is_dyn_method, lookup_impl_method}, traits::FnTrait, CallableDefId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, TyExt, + TraitEnvironment, Ty, TyBuilder, TyExt, GenericArgData, }; use super::{ @@ -34,6 +34,15 @@ use super::{ Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp, }; +macro_rules! from_bytes { + ($ty:tt, $value:expr) => { + ($ty::from_le_bytes(match ($value).try_into() { + Ok(x) => x, + Err(_) => return Err(MirEvalError::TypeError("mismatched size")), + })) + }; +} + #[derive(Debug, Default)] struct VTableMap { ty_to_id: HashMap, @@ -54,6 +63,11 @@ impl VTableMap { fn ty(&self, id: usize) -> Result<&Ty> { self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id)) } + + fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> { + let id = from_bytes!(usize, bytes); + self.ty(id) + } } pub struct Evaluator<'a> { @@ -110,15 +124,6 @@ impl IntervalOrOwned { } } -macro_rules! from_bytes { - ($ty:tt, $value:expr) => { - ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, - Err(_) => return Err(MirEvalError::TypeError("mismatched size")), - })) - }; -} - impl Address { fn from_bytes(x: &[u8]) -> Result { Ok(Address::from_usize(from_bytes!(usize, x))) @@ -781,7 +786,18 @@ impl Evaluator<'_> { } _ => not_supported!("slice unsizing from non pointers"), }, - TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"), + TyKind::Dyn(_) => match ¤t_ty.data(Interner).kind { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { + let vtable = self.vtable_map.id(ty.clone()); + let addr = + self.eval_operand(operand, locals)?.get(&self)?; + let mut r = Vec::with_capacity(16); + r.extend(addr.iter().copied()); + r.extend(vtable.to_le_bytes().into_iter()); + Owned(r) + } + _ => not_supported!("dyn unsizing from non pointers"), + }, _ => not_supported!("unknown unsized cast"), } } @@ -1227,44 +1243,8 @@ impl Evaluator<'_> { 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 (imp, generic_args) = lookup_impl_method( - self.db, - self.trait_env.clone(), - 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)))? - }; - self.write_memory(dest_addr, &result)?; + .collect::>>()?; + self.exec_fn_with_args(def, arg_bytes, generic_args, locals, dest_addr)?; } CallableDefId::StructId(id) => { let (size, variant_layout, tag) = @@ -1284,6 +1264,77 @@ impl Evaluator<'_> { Ok(()) } + fn exec_fn_with_args( + &mut self, + def: FunctionId, + arg_bytes: Vec>, + generic_args: Substitution, + locals: &Locals<'_>, + dest_addr: Address, + ) -> Result<()> { + 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.iter().cloned(), + generic_args, + &locals, + )? + } else if let Some(x) = self.detect_lang_function(def) { + self.exec_lang_item(x, &arg_bytes)? + } else { + if let Some(self_ty_idx) = + is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) + { + // In the layout of current possible receiver, which at the moment of writing this code is one of + // `&T`, `&mut T`, `Box`, `Rc`, `Arc`, and `Pin

` where `P` is one of possible recievers, + // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on + // the type. + let ty = self + .vtable_map + .ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + let ty = GenericArgData::Ty(ty.clone()).intern(Interner); + let mut args_for_target = arg_bytes; + args_for_target[0] = args_for_target[0][0..self.ptr_size()].to_vec(); + let generics_for_target = Substitution::from_iter( + Interner, + generic_args + .iter(Interner) + .enumerate() + .map(|(i, x)| if i == self_ty_idx { &ty } else { x }) + ); + return self.exec_fn_with_args( + def, + args_for_target, + generics_for_target, + locals, + dest_addr, + ); + } + let (imp, generic_args) = + lookup_impl_method(self.db, self.trait_env.clone(), 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.iter().cloned(), generic_args) + .map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))? + }; + self.write_memory(dest_addr, &result)?; + Ok(()) + } + fn exec_fn_trait( &mut self, ft: FnTrait, @@ -1317,12 +1368,9 @@ impl Evaluator<'_> { Ok(()) } - fn exec_lang_item( - &self, - x: LangItem, - mut args: std::vec::IntoIter>, - ) -> Result> { + fn exec_lang_item(&self, x: LangItem, args: &[Vec]) -> Result> { use LangItem::*; + let mut args = args.iter(); match x { PanicFmt | BeginPanic => Err(MirEvalError::Panic), SliceLen => { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 7a5ca0894200c..4fc3c67a6e1eb 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -230,7 +230,14 @@ impl MirLowerCtx<'_> { self.lower_const(c, current, place, expr_id.into())?; return Ok(Some(current)) }, - _ => not_supported!("associated functions and types"), + hir_def::AssocItemId::FunctionId(_) => { + // FnDefs are zero sized, no action is needed. + return Ok(Some(current)) + } + hir_def::AssocItemId::TypeAliasId(_) => { + // FIXME: If it is unreachable, use proper error instead of `not_supported`. + not_supported!("associated functions and types") + }, } } else if let Some(variant) = self .infer From 513e340bd344ee373847953baead3097f0b43815 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 14 Mar 2023 12:49:09 +0330 Subject: [PATCH 011/806] implement transmute intrinsic --- crates/hir-ty/src/mir/eval.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 7293156a978f9..3001832d7953d 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1187,7 +1187,7 @@ impl Evaluator<'_> { fn exec_intrinsic( &self, as_str: &str, - _arg_bytes: impl Iterator>, + mut arg_bytes: impl Iterator>, generic_args: Substitution, locals: &Locals<'_>, ) -> Result> { @@ -1202,6 +1202,12 @@ impl Evaluator<'_> { None => return Err(MirEvalError::TypeError("size_of arg is unsized")), } } + "transmute" => { + let Some(arg) = arg_bytes.next() else { + return Err(MirEvalError::TypeError("trasmute arg is not provided")); + }; + Ok(arg) + } _ => not_supported!("unknown intrinsic {as_str}"), } } From 051dae222164a04c703c5c0914a304d35206a62f Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 14 Mar 2023 17:02:38 +0330 Subject: [PATCH 012/806] Support record pattern MIR lowering --- crates/hir-ty/src/consteval/tests.rs | 41 +++++- crates/hir-ty/src/method_resolution.rs | 13 +- crates/hir-ty/src/mir/eval.rs | 15 +- crates/hir-ty/src/mir/lower.rs | 191 +++++++++++++++++++------ lib/la-arena/src/lib.rs | 2 +- 5 files changed, 200 insertions(+), 62 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index f7914b578e437..1d298f96091ae 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -555,6 +555,38 @@ fn structs() { "#, 17, ); + check_number( + r#" + struct Point { + x: i32, + y: i32, + } + + const GOAL: i32 = { + let p = Point { x: 5, y: 2 }; + let p2 = Point { x: 3, ..p }; + p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y + }; + "#, + 5232, + ); + check_number( + r#" + struct Point { + x: i32, + y: i32, + } + + const GOAL: i32 = { + let p = Point { x: 5, y: 2 }; + let Point { x, y } = p; + let Point { x: x2, .. } = p; + let Point { y: y2, .. } = p; + x * 1000 + y * 100 + x2 * 10 + y2 + }; + "#, + 5252, + ); } #[test] @@ -599,13 +631,14 @@ fn tuples() { ); check_number( r#" - struct TupleLike(i32, u8, i64, u16); - const GOAL: u8 = { + struct TupleLike(i32, i64, u8, u16); + const GOAL: i64 = { let a = TupleLike(10, 20, 3, 15); - a.1 + let TupleLike(b, .., c) = a; + a.1 * 100 + b as i64 + c as i64 }; "#, - 20, + 2025, ); check_number( r#" diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 6244b98104fe6..2003d24038b1d 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -711,12 +711,13 @@ pub fn is_dyn_method( }; let self_ty = trait_ref.self_type_parameter(Interner); if let TyKind::Dyn(d) = self_ty.kind(Interner) { - let is_my_trait_in_bounds = d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { - // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter - // what the generics are, we are sure that the method is come from the vtable. - WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, - _ => false, - }); + let is_my_trait_in_bounds = + d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { + // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter + // what the generics are, we are sure that the method is come from the vtable. + WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, + _ => false, + }); if is_my_trait_in_bounds { return Some(fn_params); } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 3001832d7953d..787665bb637a4 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -25,8 +25,8 @@ use crate::{ mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_method}, traits::FnTrait, - CallableDefId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, TyExt, GenericArgData, + CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; use super::{ @@ -1315,10 +1315,13 @@ impl Evaluator<'_> { args_for_target[0] = args_for_target[0][0..self.ptr_size()].to_vec(); let generics_for_target = Substitution::from_iter( Interner, - generic_args - .iter(Interner) - .enumerate() - .map(|(i, x)| if i == self_ty_idx { &ty } else { x }) + generic_args.iter(Interner).enumerate().map(|(i, x)| { + if i == self_ty_idx { + &ty + } else { + x + } + }), ); return self.exec_fn_with_args( def, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4fc3c67a6e1eb..d36d9e946ca8c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,16 +4,17 @@ use std::{iter, mem, sync::Arc}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ + adt::VariantData, body::Body, expr::{ Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, - RecordLitField, + RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, layout::LayoutError, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, TraitId, + DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -106,6 +107,12 @@ impl MirLowerError { type Result = std::result::Result; +enum AdtPatternShape<'a> { + Tuple { args: &'a [PatId], ellipsis: Option }, + Record { args: &'a [RecordFieldPat] }, + Unit, +} + impl MirLowerCtx<'_> { fn temp(&mut self, ty: Ty) -> Result { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { @@ -444,7 +451,8 @@ impl MirLowerCtx<'_> { current, pat.into(), Some(end), - &[pat], &None)?; + AdtPatternShape::Tuple { args: &[pat], ellipsis: None }, + )?; if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { this.set_goto(block, begin); } @@ -573,7 +581,17 @@ impl MirLowerCtx<'_> { Ok(None) } Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, .. } => { + Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { + let spread_place = match spread { + &Some(x) => { + let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { + return Ok(None); + }; + current = c; + Some(p) + }, + None => None, + }; let variant_id = self .infer .variant_resolution_for_expr(expr_id) @@ -603,9 +621,24 @@ impl MirLowerCtx<'_> { place, Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst), - operands.into_iter().map(|x| x).collect::>().ok_or( - MirLowerError::TypeError("missing field in record literal"), - )?, + match spread_place { + Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { + match x { + Some(x) => x, + None => { + let mut p = sp.clone(); + p.projection.push(ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), + })); + Operand::Copy(p) + }, + } + }).collect(), + None => operands.into_iter().map(|x| x).collect::>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + }, ), expr_id.into(), ); @@ -1021,14 +1054,11 @@ impl MirLowerCtx<'_> { 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(), - ) - }), + args, *ellipsis, + subst.iter(Interner).enumerate().map(|(i, x)| { + (PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone()) + }), &cond_place, binding_mode, )? @@ -1062,7 +1092,21 @@ impl MirLowerCtx<'_> { } (then_target, current_else) } - Pat::Record { .. } => not_supported!("record pattern"), + Pat::Record { args, .. } => { + 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, + AdtPatternShape::Record { args: &*args }, + )? + } Pat::Range { .. } => not_supported!("range pattern"), Pat::Slice { .. } => not_supported!("slice pattern"), Pat::Path(_) => { @@ -1077,8 +1121,7 @@ impl MirLowerCtx<'_> { current, pattern.into(), current_else, - &[], - &None, + AdtPatternShape::Unit, )? } Pat::Lit(l) => { @@ -1160,8 +1203,7 @@ impl MirLowerCtx<'_> { current, pattern.into(), current_else, - args, - ellipsis, + AdtPatternShape::Tuple { args, ellipsis: *ellipsis }, )? } Pat::Ref { .. } => not_supported!("& pattern"), @@ -1179,15 +1221,13 @@ impl MirLowerCtx<'_> { current: BasicBlockId, span: MirSpan, current_else: Option, - args: &[PatId], - ellipsis: &Option, + shape: AdtPatternShape<'_>, ) -> 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; @@ -1208,57 +1248,99 @@ impl MirLowerCtx<'_> { }, ); 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( + self.pattern_matching_variant_fields( + shape, + &enum_data.variants[v.local_id].variant_data, + variant, + subst, 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, _)| { + self.pattern_matching_variant_fields( + shape, + &struct_data.variant_data, + variant, + subst, + current, + current_else, + &cond_place, + binding_mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + + fn pattern_matching_variant_fields( + &mut self, + shape: AdtPatternShape<'_>, + variant_data: &VariantData, + v: VariantId, + subst: &Substitution, + current: BasicBlockId, + current_else: Option, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + let fields_type = self.db.field_types(v); + Ok(match shape { + AdtPatternShape::Record { args } => { + let it = args + .iter() + .map(|x| { + let field_id = + variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + Ok(( + PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), + x.pat, + fields_type[field_id].clone().substitute(Interner, subst), + )) + }) + .collect::>>()?; + self.pattern_match_adt( + current, + current_else, + it.into_iter(), + cond_place, + binding_mode, + )? + } + AdtPatternShape::Tuple { args, ellipsis } => { + let fields = variant_data.fields().iter().map(|(x, _)| { ( - PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), + PlaceElem::Field(FieldId { parent: v.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, + args, + ellipsis, + fields, + cond_place, binding_mode, )? } - VariantId::UnionId(_) => { - return Err(MirLowerError::TypeError("pattern matching on union")) - } + AdtPatternShape::Unit => (current, current_else), }) } - fn pattern_match_tuple_like( + fn pattern_match_adt( &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); @@ -1268,6 +1350,25 @@ impl MirLowerCtx<'_> { Ok((current, current_else)) } + fn pattern_match_tuple_like( + &mut self, + current: BasicBlockId, + current_else: Option, + args: &[PatId], + ellipsis: Option, + fields: impl DoubleEndedIterator + Clone, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())) + .map(|(x, y)| (y.0, *x, y.1)); + self.pattern_match_adt(current, current_else, it, cond_place, binding_mode) + } + fn discr_temp_place(&mut self) -> Place { match &self.discr_temp { Some(x) => x.clone(), diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs index ccaaf3991769e..f6597efd8fd27 100644 --- a/lib/la-arena/src/lib.rs +++ b/lib/la-arena/src/lib.rs @@ -295,7 +295,7 @@ impl Arena { /// ``` pub fn iter( &self, - ) -> impl Iterator, &T)> + ExactSizeIterator + DoubleEndedIterator { + ) -> impl Iterator, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone { self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value)) } From 9564773d5e6dff6d430594028383315ca2e202ef Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 14 Mar 2023 23:01:46 +0330 Subject: [PATCH 013/806] Improve pattern matching MIR lowering --- crates/hir-def/src/body/lower.rs | 19 +- crates/hir-def/src/expr.rs | 10 + crates/hir-ty/src/consteval/tests.rs | 112 +++++ crates/hir-ty/src/mir/eval.rs | 10 +- crates/hir-ty/src/mir/lower.rs | 415 ++---------------- .../hir-ty/src/mir/lower/pattern_matching.rs | 399 +++++++++++++++++ crates/hir-ty/src/mir/pretty.rs | 14 +- crates/syntax/src/ast/generated/nodes.rs | 1 + crates/test-utils/src/minicore.rs | 5 +- 9 files changed, 590 insertions(+), 395 deletions(-) create mode 100644 crates/hir-ty/src/mir/lower/pattern_matching.rs diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 348b7589ff4f4..7cce23b5316cf 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1030,9 +1030,16 @@ impl ExprCollector<'_> { .collect(), } } - ast::Pat::LiteralPat(lit) => { + ast::Pat::LiteralPat(lit) => 'b: { if let Some(ast_lit) = lit.literal() { - let expr = Expr::Literal(ast_lit.kind().into()); + let mut hir_lit: Literal = ast_lit.kind().into(); + if lit.minus_token().is_some() { + let Some(h) = hir_lit.negate() else { + break 'b Pat::Missing; + }; + hir_lit = h; + } + let expr = Expr::Literal(hir_lit); let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); let expr_id = self.alloc_expr(expr, expr_ptr); Pat::Lit(expr_id) @@ -1144,11 +1151,11 @@ impl From for Literal { FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), builtin, ) - } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) { - Literal::Int(lit.value().unwrap_or(0) as i128, builtin) - } else { - let builtin = lit.suffix().and_then(BuiltinUint::from_suffix); + } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) { Literal::Uint(lit.value().unwrap_or(0), builtin) + } else { + let builtin = lit.suffix().and_then(BuiltinInt::from_suffix); + Literal::Int(lit.value().unwrap_or(0) as i128, builtin) } } LiteralKind::FloatNumber(lit) => { diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index 5b8758224371b..8b1528f81e61e 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -92,6 +92,16 @@ pub enum Literal { Float(FloatTypeWrapper, Option), } +impl Literal { + pub fn negate(self) -> Option { + if let Literal::Int(i, k) = self { + Some(Literal::Int(-i, k)) + } else { + None + } + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if the syntax tree does not have a required expression piece. diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 1d298f96091ae..ecc163a4151cf 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -685,6 +685,36 @@ fn path_pattern_matching() { ); } +#[test] +fn pattern_matching_literal() { + check_number( + r#" + const fn f(x: i32) -> i32 { + match x { + -1 => 1, + 1 => 10, + _ => 100, + } + } + const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5); + "#, + 211 + ); + check_number( + r#" + const fn f(x: &str) -> u8 { + match x { + "foo" => 1, + "bar" => 10, + _ => 100, + } + } + const GOAL: u8 = f("foo") + f("bar"); + "#, + 11 + ); +} + #[test] fn pattern_matching_ergonomics() { check_number( @@ -698,6 +728,16 @@ fn pattern_matching_ergonomics() { "#, 5, ); + check_number( + r#" + const GOAL: u8 = { + let a = &(2, 3); + let &(x, y) = a; + x + y + }; + "#, + 5, + ); } #[test] @@ -781,6 +821,33 @@ fn function_param_patterns() { ); } +#[test] +fn match_guards() { + check_number( + r#" + //- minicore: option, eq + impl PartialEq for Option { + fn eq(&self, other: &Rhs) -> bool { + match (self, other) { + (Some(x), Some(y)) => x == y, + (None, None) => true, + _ => false, + } + } + } + fn f(x: Option) -> i32 { + match x { + y if y == Some(42) => 42000, + Some(y) => y, + None => 10 + } + } + const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None); + "#, + 42012, + ); +} + #[test] fn options() { check_number( @@ -983,6 +1050,51 @@ fn function_pointer() { ); } +#[test] +fn enum_variant_as_function() { + check_number( + r#" + //- minicore: option + const GOAL: u8 = { + let f = Some; + f(3).unwrap_or(2) + }; + "#, + 3, + ); + check_number( + r#" + //- minicore: option + const GOAL: u8 = { + let f: fn(u8) -> Option = Some; + f(3).unwrap_or(2) + }; + "#, + 3, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + enum Foo { + Add2(u8), + Mult3(u8), + } + use Foo::*; + const fn f(x: Foo) -> u8 { + match x { + Add2(x) => x + 2, + Mult3(x) => x * 3, + } + } + const GOAL: u8 = { + let x = [Add2, Mult3]; + f(x[0](1)) + f(x[1](5)) + }; + "#, + 18, + ); +} + #[test] fn function_traits() { check_number( diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 787665bb637a4..450cd5404e5e6 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -423,6 +423,7 @@ impl Evaluator<'_> { args: impl Iterator>, subst: Substitution, ) -> Result> { + dbg!(body.dbg(self.db)); if let Some(x) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = x; } else { @@ -581,7 +582,14 @@ impl Evaluator<'_> { 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")?; + let size = if ty.kind(Interner) == &TyKind::Str { + let ns = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + lc = &lc[..self.ptr_size()]; + rc = &rc[..self.ptr_size()]; + ns + } else { + 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)?; } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index d36d9e946ca8c..4da0f87609974 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,7 +4,7 @@ use std::{iter, mem, sync::Arc}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - adt::VariantData, + adt::{VariantData, StructKind}, body::Body, expr::{ Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, @@ -28,6 +28,9 @@ use crate::{ use super::*; mod as_place; +mod pattern_matching; + +use pattern_matching::AdtPatternShape; #[derive(Debug, Clone, Copy)] struct LoopBlocks { @@ -107,12 +110,6 @@ impl MirLowerError { type Result = std::result::Result; -enum AdtPatternShape<'a> { - Tuple { args: &'a [PatId], ellipsis: Option }, - Record { args: &'a [RecordFieldPat] }, - Unit, -} - impl MirLowerCtx<'_> { fn temp(&mut self, ty: Ty) -> Result { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { @@ -275,15 +272,19 @@ impl MirLowerCtx<'_> { 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(), - )?; + let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; + if variant_data.variant_data.kind() == StructKind::Unit { + let ty = self.infer.type_of_expr[expr_id].clone(); + current = self.lower_enum_variant( + variant_id, + current, + place, + ty, + vec![], + expr_id.into(), + )?; + } + // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed Ok(Some(current)) } ValueNs::GenericParam(p) => { @@ -517,10 +518,7 @@ impl MirLowerCtx<'_> { 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( + let (then, mut otherwise) = self.pattern_match( current, None, cond_place.clone(), @@ -528,6 +526,16 @@ impl MirLowerCtx<'_> { *pat, BindingAnnotation::Unannotated, )?; + let then = if let &Some(guard) = guard { + let next = self.new_basic_block(); + let o = otherwise.get_or_insert_with(|| self.new_basic_block()); + if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { + self.set_terminator(c, Terminator::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }); + } + next + } else { + then + }; 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); @@ -922,7 +930,7 @@ impl MirLowerCtx<'_> { ) -> Result { let subst = match ty.kind(Interner) { TyKind::Adt(_, subst) => subst.clone(), - _ => not_supported!("Non ADT enum"), + _ => implementation_error!("Non ADT enum"), }; self.push_assignment( prev_block, @@ -1020,355 +1028,6 @@ impl MirLowerCtx<'_> { 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, - *ellipsis, - subst.iter(Interner).enumerate().map(|(i, x)| { - (PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone()) - }), - &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 { args, .. } => { - 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, - AdtPatternShape::Record { args: &*args }, - )? - } - 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, - AdtPatternShape::Unit, - )? - } - 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, - AdtPatternShape::Tuple { args, ellipsis: *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, - shape: AdtPatternShape<'_>, - ) -> 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")), - }; - 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); - self.pattern_matching_variant_fields( - shape, - &enum_data.variants[v.local_id].variant_data, - variant, - subst, - next, - Some(else_target), - &cond_place, - binding_mode, - )? - } - VariantId::StructId(s) => { - let struct_data = self.db.struct_data(s); - self.pattern_matching_variant_fields( - shape, - &struct_data.variant_data, - variant, - subst, - current, - current_else, - &cond_place, - binding_mode, - )? - } - VariantId::UnionId(_) => { - return Err(MirLowerError::TypeError("pattern matching on union")) - } - }) - } - - fn pattern_matching_variant_fields( - &mut self, - shape: AdtPatternShape<'_>, - variant_data: &VariantData, - v: VariantId, - subst: &Substitution, - current: BasicBlockId, - current_else: Option, - cond_place: &Place, - binding_mode: BindingAnnotation, - ) -> Result<(BasicBlockId, Option)> { - let fields_type = self.db.field_types(v); - Ok(match shape { - AdtPatternShape::Record { args } => { - let it = args - .iter() - .map(|x| { - let field_id = - variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; - Ok(( - PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), - x.pat, - fields_type[field_id].clone().substitute(Interner, subst), - )) - }) - .collect::>>()?; - self.pattern_match_adt( - current, - current_else, - it.into_iter(), - cond_place, - binding_mode, - )? - } - AdtPatternShape::Tuple { args, ellipsis } => { - let fields = 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( - current, - current_else, - args, - ellipsis, - fields, - cond_place, - binding_mode, - )? - } - AdtPatternShape::Unit => (current, current_else), - }) - } - - fn pattern_match_adt( - &mut self, - mut current: BasicBlockId, - mut current_else: Option, - args: impl Iterator, - cond_place: &Place, - binding_mode: BindingAnnotation, - ) -> Result<(BasicBlockId, Option)> { - 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 pattern_match_tuple_like( - &mut self, - current: BasicBlockId, - current_else: Option, - args: &[PatId], - ellipsis: Option, - fields: impl DoubleEndedIterator + Clone, - cond_place: &Place, - binding_mode: BindingAnnotation, - ) -> Result<(BasicBlockId, Option)> { - let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); - let it = al - .iter() - .zip(fields.clone()) - .chain(ar.iter().rev().zip(fields.rev())) - .map(|(x, y)| (y.0, *x, y.1)); - self.pattern_match_adt(current, current_else, it, cond_place, binding_mode) - } - fn discr_temp_place(&mut self) -> Place { match &self.discr_temp { Some(x) => x.clone(), @@ -1546,22 +1205,6 @@ impl MirLowerCtx<'_> { } } -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) { diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs new file mode 100644 index 0000000000000..c3ced82aab7bd --- /dev/null +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -0,0 +1,399 @@ +//! MIR lowering for patterns + +use super::*; + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +pub(super) enum AdtPatternShape<'a> { + Tuple { args: &'a [PatId], ellipsis: Option }, + Record { args: &'a [RecordFieldPat] }, + Unit, +} + +impl MirLowerCtx<'_> { + /// 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. + pub(super) 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, + *ellipsis, + subst.iter(Interner).enumerate().map(|(i, x)| { + (PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone()) + }), + &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 { args, .. } => { + 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, + AdtPatternShape::Record { args: &*args }, + )? + } + 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, + AdtPatternShape::Unit, + )? + } + Pat::Lit(l) => match &self.body.exprs[*l] { + Expr::Literal(l) => { + let c = self.lower_literal_to_operand(cond_ty, l)?; + self.pattern_match_const(current_else, current, c, cond_place, pattern)? + } + _ => not_supported!("expression path literal"), + }, + 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, + AdtPatternShape::Tuple { args, ellipsis: *ellipsis }, + )? + } + Pat::Ref { pat, mutability: _ } => { + if let Some((ty, _, _)) = cond_ty.as_reference() { + cond_ty = ty.clone(); + cond_place.projection.push(ProjectionElem::Deref); + self.pattern_match( + current, + current_else, + cond_place, + cond_ty, + *pat, + binding_mode, + )? + } else { + return Err(MirLowerError::TypeError("& pattern for non reference")); + } + } + Pat::Box { .. } => not_supported!("box pattern"), + Pat::ConstBlock(_) => not_supported!("const block pattern"), + }) + } + + fn pattern_match_const( + &mut self, + current_else: Option, + current: BasicBlockId, + c: Operand, + cond_place: Place, + pattern: Idx, + ) -> Result<(BasicBlockId, Option)> { + let then_target = self.new_basic_block(); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + let discr: Place = self.temp(TyBuilder::bool())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + Terminator::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, then_target, else_target), + }, + ); + Ok((then_target, Some(else_target))) + } + + pub(super) 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, + shape: AdtPatternShape<'_>, + ) -> 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")), + }; + 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); + self.pattern_matching_variant_fields( + shape, + &enum_data.variants[v.local_id].variant_data, + variant, + subst, + next, + Some(else_target), + &cond_place, + binding_mode, + )? + } + VariantId::StructId(s) => { + let struct_data = self.db.struct_data(s); + self.pattern_matching_variant_fields( + shape, + &struct_data.variant_data, + variant, + subst, + current, + current_else, + &cond_place, + binding_mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + + fn pattern_matching_variant_fields( + &mut self, + shape: AdtPatternShape<'_>, + variant_data: &VariantData, + v: VariantId, + subst: &Substitution, + current: BasicBlockId, + current_else: Option, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + let fields_type = self.db.field_types(v); + Ok(match shape { + AdtPatternShape::Record { args } => { + let it = args + .iter() + .map(|x| { + let field_id = + variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + Ok(( + PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), + x.pat, + fields_type[field_id].clone().substitute(Interner, subst), + )) + }) + .collect::>>()?; + self.pattern_match_adt( + current, + current_else, + it.into_iter(), + cond_place, + binding_mode, + )? + } + AdtPatternShape::Tuple { args, ellipsis } => { + let fields = 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( + current, + current_else, + args, + ellipsis, + fields, + cond_place, + binding_mode, + )? + } + AdtPatternShape::Unit => (current, current_else), + }) + } + + fn pattern_match_adt( + &mut self, + mut current: BasicBlockId, + mut current_else: Option, + args: impl Iterator, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + 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 pattern_match_tuple_like( + &mut self, + current: BasicBlockId, + current_else: Option, + args: &[PatId], + ellipsis: Option, + fields: impl DoubleEndedIterator + Clone, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())) + .map(|(x, y)| (y.0, *x, y.1)); + self.pattern_match_adt(current, current_else, it, cond_place, binding_mode) + } +} + +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); + } +} diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index eb0002266dc56..ab3bb643e7a7d 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -1,6 +1,6 @@ //! A pretty-printer for MIR. -use std::fmt::{Display, Write}; +use std::fmt::{Display, Write, Debug}; use hir_def::{body::Body, expr::BindingId}; use hir_expand::name::Name; @@ -23,6 +23,18 @@ impl MirBody { ctx.for_body(); ctx.result } + + // String with lines is rendered poorly in `dbg!` macros, which I use very much, so this + // function exists to solve that. + pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug { + struct StringDbg(String); + impl Debug for StringDbg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } + } + StringDbg(self.pretty_print(db)) + } } struct MirPrettyCtx<'a> { diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index fe324845360d7..a2124f3ac164d 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1376,6 +1376,7 @@ pub struct LiteralPat { } impl LiteralPat { pub fn literal(&self) -> Option { support::child(&self.syntax) } + pub fn minus_token(&self) -> Option { support::token(&self.syntax, T![-]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 8dd9f306c8a62..bf592c1f7be87 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -597,7 +597,10 @@ pub mod option { loop {} } pub fn unwrap_or(self, default: T) -> T { - loop {} + match self { + Some(val) => val, + None => default, + } } // region:fn pub fn and_then(self, f: F) -> Option From eb4939e217960ee77d79ec436a39f3cead646de4 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 17 Mar 2023 14:02:55 +0330 Subject: [PATCH 014/806] Support overloaded deref MIR lowering --- crates/hir-def/src/body/lower.rs | 42 ++-- crates/hir-ty/src/consteval/tests.rs | 27 ++- crates/hir-ty/src/infer.rs | 3 + crates/hir-ty/src/infer/expr.rs | 47 ++++- crates/hir-ty/src/infer/mutability.rs | 182 ++++++++++++++++++ crates/hir-ty/src/mir/eval.rs | 33 ++-- crates/hir-ty/src/mir/lower.rs | 2 +- crates/hir-ty/src/mir/lower/as_place.rs | 26 ++- crates/hir-ty/src/mir/pretty.rs | 4 +- crates/hir-ty/src/tests/coercion.rs | 3 +- crates/hir-ty/src/tests/method_resolution.rs | 3 +- .../src/handlers/mutability_errors.rs | 31 ++- crates/ide/src/inlay_hints/chaining.rs | 12 +- crates/ide/src/syntax_highlighting/tests.rs | 2 +- crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 2 +- crates/syntax/src/tests/sourcegen_ast.rs | 1 + crates/test-utils/src/minicore.rs | 62 ++++++ 18 files changed, 398 insertions(+), 86 deletions(-) create mode 100644 crates/hir-ty/src/infer/mutability.rs diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 7cce23b5316cf..448821d3844ab 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -609,21 +609,12 @@ impl ExprCollector<'_> { fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: { if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) { - if let Some(cf_continue) = - LangItem::ControlFlowContinue.path(self.db, self.krate) - { - if let Some(cf_break) = - LangItem::ControlFlowBreak.path(self.db, self.krate) - { + if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) { + if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) { if let Some(try_from_residual) = LangItem::TryTraitFromResidual.path(self.db, self.krate) { - break 'if_chain ( - try_branch, - cf_continue, - cf_break, - try_from_residual, - ); + break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual); } } } @@ -634,15 +625,10 @@ impl ExprCollector<'_> { let operand = self.collect_expr_opt(e.expr()); let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone()); let expr = self.alloc_expr( - Expr::Call { - callee: try_branch, - args: Box::new([operand]), - is_assignee_expr: false, - }, + Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false }, syntax_ptr.clone(), ); - let continue_binding = - self.alloc_binding(name![v1], BindingAnnotation::Unannotated); + let continue_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated); let continue_bpat = self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None }); self.add_definition_to_binding(continue_binding, continue_bpat); @@ -656,8 +642,7 @@ impl ExprCollector<'_> { expr: self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()), }; let break_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated); - let break_bpat = - self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); + let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); self.add_definition_to_binding(break_binding, break_bpat); let break_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { @@ -667,10 +652,8 @@ impl ExprCollector<'_> { }), guard: None, expr: { - let x = - self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()); - let callee = - self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); + let x = self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()); + let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); let result = self.alloc_expr( Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, syntax_ptr.clone(), @@ -1030,8 +1013,9 @@ impl ExprCollector<'_> { .collect(), } } - ast::Pat::LiteralPat(lit) => 'b: { - if let Some(ast_lit) = lit.literal() { + // FIXME: rustfmt removes this label if it is a block and not a loop + ast::Pat::LiteralPat(lit) => 'b: loop { + break if let Some(ast_lit) = lit.literal() { let mut hir_lit: Literal = ast_lit.kind().into(); if lit.minus_token().is_some() { let Some(h) = hir_lit.negate() else { @@ -1045,8 +1029,8 @@ impl ExprCollector<'_> { Pat::Lit(expr_id) } else { Pat::Missing - } - } + }; + }, ast::Pat::RestPat(_) => { // `RestPat` requires special handling and should not be mapped // to a Pat. Here we are using `Pat::Missing` as a fallback for diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index ecc163a4151cf..a658bfa0c9a83 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -166,8 +166,7 @@ fn reference_autoderef() { #[test] fn overloaded_deref() { - // FIXME: We should support this. - check_fail( + check_number( r#" //- minicore: deref_mut struct Foo; @@ -185,9 +184,7 @@ fn overloaded_deref() { *y + *x }; "#, - ConstEvalError::MirLowerError(MirLowerError::NotSupported( - "explicit overloaded deref".into(), - )), + 10, ); } @@ -698,7 +695,7 @@ fn pattern_matching_literal() { } const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5); "#, - 211 + 211, ); check_number( r#" @@ -711,7 +708,7 @@ fn pattern_matching_literal() { } const GOAL: u8 = f("foo") + f("bar"); "#, - 11 + 11, ); } @@ -1116,6 +1113,22 @@ fn function_traits() { "#, 15, ); + check_number( + r#" + //- minicore: coerce_unsized, fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3); + "#, + 10, + ); check_number( r#" //- minicore: fn diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 06d74215fffbf..d1b9aff36d242 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -57,6 +57,7 @@ mod expr; mod pat; mod coerce; mod closure; +mod mutability; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { @@ -99,6 +100,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc InferenceContext<'a> { if let Some(fn_x) = func { match fn_x { FnTrait::FnOnce => (), - FnTrait::FnMut => adjustments.push(Adjustment::borrow( - Mutability::Mut, - derefed_callee.clone(), - )), - FnTrait::Fn => adjustments.push(Adjustment::borrow( - Mutability::Not, - derefed_callee.clone(), - )), + FnTrait::FnMut => { + if !matches!( + derefed_callee.kind(Interner), + TyKind::Ref(Mutability::Mut, _, _) + ) { + adjustments.push(Adjustment::borrow( + Mutability::Mut, + derefed_callee.clone(), + )); + } + } + FnTrait::Fn => { + if !matches!( + derefed_callee.kind(Interner), + TyKind::Ref(Mutability::Not, _, _) + ) { + adjustments.push(Adjustment::borrow( + Mutability::Not, + derefed_callee.clone(), + )); + } + } } let trait_ = fn_x .get_id(self.db, self.trait_env.krate) @@ -673,6 +687,23 @@ impl<'a> InferenceContext<'a> { // FIXME: Note down method resolution her match op { UnaryOp::Deref => { + if let Some(deref_trait) = self + .db + .lang_item(self.table.trait_env.krate, LangItem::Deref) + .and_then(|l| l.as_trait()) + { + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name(&name![deref]) + { + // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that + // the mutability is not wrong, and will be fixed in `self.infer_mut`). + self.write_method_resolution( + tgt_expr, + deref_fn, + Substitution::empty(Interner), + ); + } + } autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty()) } UnaryOp::Neg => { diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs new file mode 100644 index 0000000000000..8e3d71788f2f0 --- /dev/null +++ b/crates/hir-ty/src/infer/mutability.rs @@ -0,0 +1,182 @@ +//! Finds if an expression is an immutable context or a mutable context, which is used in selecting +//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar. + +use chalk_ir::Mutability; +use hir_def::{ + expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, + lang_item::LangItem, +}; +use hir_expand::name; + +use crate::{lower::lower_to_chalk_mutability, Adjust, AutoBorrow, OverloadedDeref}; + +use super::InferenceContext; + +impl<'a> InferenceContext<'a> { + pub(crate) fn infer_mut_body(&mut self) { + self.infer_mut_expr(self.body.body_expr, Mutability::Not); + } + + fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) { + let mut v = vec![]; + let adjustments = self.result.expr_adjustments.get_mut(&tgt_expr).unwrap_or(&mut v); + for adj in adjustments.iter_mut().rev() { + match &mut adj.kind { + Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (), + Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)), + Adjust::Borrow(b) => match b { + AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m, + }, + } + } + self.infer_mut_expr_without_adjust(tgt_expr, mutability); + } + + fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { + match &self.body[tgt_expr] { + Expr::Missing => (), + &Expr::If { condition, then_branch, else_branch } => { + self.infer_mut_expr(condition, Mutability::Not); + self.infer_mut_expr(then_branch, Mutability::Not); + if let Some(else_branch) = else_branch { + self.infer_mut_expr(else_branch, Mutability::Not); + } + } + Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), + Expr::Block { id: _, statements, tail, label: _ } + | Expr::TryBlock { id: _, statements, tail } + | Expr::Async { id: _, statements, tail } + | Expr::Const { id: _, statements, tail } + | Expr::Unsafe { id: _, statements, tail } => { + for st in statements.iter() { + match st { + Statement::Let { pat, type_ref: _, initializer, else_branch } => { + if let Some(i) = initializer { + self.infer_mut_expr(*i, self.pat_bound_mutability(*pat)); + } + if let Some(e) = else_branch { + self.infer_mut_expr(*e, Mutability::Not); + } + } + Statement::Expr { expr, has_semi: _ } => { + self.infer_mut_expr(*expr, Mutability::Not); + } + } + } + if let Some(tail) = tail { + self.infer_mut_expr(*tail, Mutability::Not); + } + } + &Expr::For { iterable: c, pat: _, body, label: _ } + | &Expr::While { condition: c, body, label: _ } => { + self.infer_mut_expr(c, Mutability::Not); + self.infer_mut_expr(body, Mutability::Not); + } + Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ } + | Expr::Call { callee: x, args, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x))); + } + Expr::Match { expr, arms } => { + let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat)); + self.infer_mut_expr(*expr, m); + for arm in arms.iter() { + self.infer_mut_expr(arm.expr, Mutability::Not); + } + } + Expr::Yield { expr } + | Expr::Yeet { expr } + | Expr::Return { expr } + | Expr::Break { expr, label: _ } => { + if let &Some(expr) = expr { + self.infer_mut_expr(expr, Mutability::Not); + } + } + Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread)) + } + &Expr::Index { base, index } => { + self.infer_mut_expr(base, mutability); + self.infer_mut_expr(index, Mutability::Not); + } + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { + if mutability == Mutability::Mut { + if let Some(deref_trait) = self + .db + .lang_item(self.table.trait_env.krate, LangItem::DerefMut) + .and_then(|l| l.as_trait()) + { + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + { + *f = deref_fn; + } + } + } + } + self.infer_mut_expr(*expr, mutability); + } + Expr::Field { expr, name: _ } => { + self.infer_mut_expr(*expr, mutability); + } + Expr::UnaryOp { expr, op: _ } + | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ } + | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } + | Expr::Await { expr } + | Expr::Box { expr } + | Expr::Loop { body: expr, label: _ } + | Expr::Cast { expr, type_ref: _ } => { + self.infer_mut_expr(*expr, Mutability::Not); + } + Expr::Ref { expr, rawness: _, mutability } => { + let mutability = lower_to_chalk_mutability(*mutability); + self.infer_mut_expr(*expr, mutability); + } + Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs }) + | Expr::BinaryOp { lhs, rhs, op: _ } + | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => { + self.infer_mut_expr(*lhs, Mutability::Not); + self.infer_mut_expr(*rhs, Mutability::Not); + } + // not implemented + Expr::Closure { .. } => (), + Expr::Tuple { exprs, is_assignee_expr: _ } + | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => { + self.infer_mut_not_expr_iter(exprs.iter().copied()); + } + // These don't need any action, as they don't have sub expressions + Expr::Range { lhs: None, rhs: None, range_type: _ } + | Expr::Literal(_) + | Expr::Path(_) + | Expr::Continue { .. } + | Expr::Underscore => (), + } + } + + fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator) { + for expr in exprs { + self.infer_mut_expr(expr, Mutability::Not); + } + } + + fn pat_iter_bound_mutability(&self, mut pat: impl Iterator) -> Mutability { + if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) { + Mutability::Mut + } else { + Mutability::Not + } + } + + /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions + /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in + /// `let (ref x0, ref x1) = *x;` we should use `Deref`. + fn pat_bound_mutability(&self, pat: PatId) -> Mutability { + let mut r = Mutability::Not; + self.body.walk_bindings_in_pat(pat, |b| { + if self.body.bindings[b].mode == BindingAnnotation::RefMut { + r = Mutability::Mut; + } + }); + r + } +} diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 450cd5404e5e6..f8545e88ad548 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,6 +1,6 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, iter, sync::Arc}; +use std::{borrow::Cow, collections::HashMap, iter, ops::Range, sync::Arc}; use base_db::CrateId; use chalk_ir::{ @@ -109,6 +109,10 @@ impl Interval { fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { memory.read_memory(self.addr, self.size) } + + fn slice(self, range: Range) -> Interval { + Interval { addr: self.addr.offset(range.start), size: range.len() } + } } enum IntervalOrOwned { @@ -423,7 +427,6 @@ impl Evaluator<'_> { args: impl Iterator>, subst: Substitution, ) -> Result> { - dbg!(body.dbg(self.db)); if let Some(x) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = x; } else { @@ -1360,24 +1363,24 @@ impl Evaluator<'_> { locals: &Locals<'_>, ) -> Result<()> { let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; - let ref_func_ty = self.operand_ty(func, locals)?; - let func_ty = match ft { - FnTrait::FnOnce => ref_func_ty, - FnTrait::FnMut | FnTrait::Fn => match ref_func_ty.as_reference() { - Some(x) => x.0.clone(), - None => return Err(MirEvalError::TypeError("fn trait with non-reference arg")), - }, - }; + let mut func_ty = self.operand_ty(func, locals)?; + let mut func_data = self.eval_operand(func, locals)?; + while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) { + func_ty = z.clone(); + if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) { + let id = + from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]); + func_data = func_data.slice(0..self.ptr_size()); + func_ty = self.vtable_map.ty(id)?.clone(); + } + let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; + func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; + } match &func_ty.data(Interner).kind { TyKind::FnDef(def, subst) => { self.exec_fn_def(*def, subst, destination, &args[1..], locals)?; } TyKind::Function(_) => { - let mut func_data = self.eval_operand(func, locals)?; - if let FnTrait::FnMut | FnTrait::Fn = ft { - let addr = Address::from_bytes(func_data.get(self)?)?; - func_data = Interval { addr, size: self.ptr_size() }; - } self.exec_fn_pointer(func_data, destination, &args[1..], locals)?; } x => not_supported!("Call {ft:?} trait methods with type {x:?}"), diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4da0f87609974..04175fb4d9569 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,7 +4,7 @@ use std::{iter, mem, sync::Arc}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - adt::{VariantData, StructKind}, + adt::{StructKind, VariantData}, body::Body, expr::{ Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index b683dd7f90d62..c6f4f66ada0e3 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -145,10 +145,32 @@ impl MirLowerCtx<'_> { self.expr_ty(*expr).kind(Interner), TyKind::Ref(..) | TyKind::Raw(..) ) { - let Some(_) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - not_supported!("explicit overloaded deref"); + return self.lower_overloaded_deref( + current, + p, + self.expr_ty_after_adjustments(*expr), + self.expr_ty(expr_id), + expr_id.into(), + 'b: { + if let Some((f, _)) = self.infer.method_resolution(expr_id) { + if let Some(deref_trait) = + self.resolve_lang_item(LangItem::DerefMut)?.as_trait() + { + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&name![deref_mut]) + { + break 'b deref_fn == f; + } + } + } + false + }, + ); } let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index ab3bb643e7a7d..9ec2913dce970 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -1,6 +1,6 @@ //! A pretty-printer for MIR. -use std::fmt::{Display, Write, Debug}; +use std::fmt::{Debug, Display, Write}; use hir_def::{body::Body, expr::BindingId}; use hir_expand::name::Name; @@ -24,7 +24,7 @@ impl MirBody { ctx.result } - // String with lines is rendered poorly in `dbg!` macros, which I use very much, so this + // String with lines is rendered poorly in `dbg` macros, which I use very much, so this // function exists to solve that. pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug { struct StringDbg(String); diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 696bdef03fcba..9f624cc32c0ce 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -258,7 +258,6 @@ fn test() { #[test] fn coerce_autoderef_block() { - // FIXME: We should know mutability in overloaded deref check_no_mismatches( r#" //- minicore: deref @@ -268,7 +267,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(None))), Borrow(Ref(Not)) + // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not)) } "#, ); diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 378d478336102..f3ca93672d9fc 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1255,7 +1255,6 @@ fn foo(a: &T) { #[test] fn autoderef_visibility_field() { - // FIXME: We should know mutability in overloaded deref check( r#" //- minicore: deref @@ -1277,7 +1276,7 @@ mod a { mod b { fn foo() { let x = super::a::Bar::new().0; - // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None))) + // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))) // ^^^^^^^^^^^^^^^^^^^^^^ type: char } } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 03951ea2bf51b..83c61f73db637 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -566,7 +566,6 @@ fn f(x: [(i32, u8); 10]) { #[test] fn overloaded_deref() { - // FIXME: check for false negative check_diagnostics( r#" //- minicore: deref_mut @@ -574,22 +573,36 @@ use core::ops::{Deref, DerefMut}; struct Foo; impl Deref for Foo { - type Target = i32; - fn deref(&self) -> &i32 { - &5 + type Target = (i32, u8); + fn deref(&self) -> &(i32, u8) { + &(5, 2) } } impl DerefMut for Foo { - fn deref_mut(&mut self) -> &mut i32 { - &mut 5 + fn deref_mut(&mut self) -> &mut (i32, u8) { + &mut (5, 2) } } fn f() { - let x = Foo; + let mut x = Foo; + //^^^^^ 💡 weak: variable does not need to be mutable let y = &*x; let x = Foo; - let mut x = Foo; - let y: &mut i32 = &mut x; + let y = &mut *x; + //^^ 💡 error: cannot mutate immutable variable `x` + let x = Foo; + let x = Foo; + let y: &mut (i32, u8) = &mut x; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + let ref mut y = *x; + //^^ 💡 error: cannot mutate immutable variable `x` + let (ref mut y, _) = *x; + //^^ 💡 error: cannot mutate immutable variable `x` + match *x { + //^^ 💡 error: cannot mutate immutable variable `x` + (ref y, _) => (), + (_, ref mut y) => (), + } } "#, ); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 1e1771259b1ba..11e6dc05fa106 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -435,7 +435,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 5805..5813, }, ), tooltip: "", @@ -448,7 +448,7 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 5837..5841, }, ), tooltip: "", @@ -468,7 +468,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 5805..5813, }, ), tooltip: "", @@ -481,7 +481,7 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 5837..5841, }, ), tooltip: "", @@ -501,7 +501,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 5805..5813, }, ), tooltip: "", @@ -514,7 +514,7 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 5837..5841, }, ), tooltip: "", diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index ac9bd8e39dc02..5cc3bad04be18 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() { .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) .count() }; - assert_eq!(hash, 1608); + assert_eq!(hash, 1170); } diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 548b5ba8b8b6d..1c15a606f9578 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -613,7 +613,7 @@ Pat = | ConstBlockPat LiteralPat = - Literal + '-'? Literal IdentPat = Attr* 'ref'? 'mut'? Name ('@' Pat)? diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index a2124f3ac164d..0e84aca5c7d9a 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1375,8 +1375,8 @@ pub struct LiteralPat { pub(crate) syntax: SyntaxNode, } impl LiteralPat { - pub fn literal(&self) -> Option { support::child(&self.syntax) } pub fn minus_token(&self) -> Option { support::token(&self.syntax, T![-]) } + pub fn literal(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index e954b58251faa..77a8363a185c1 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -535,6 +535,7 @@ impl Field { "!" => "excl", "*" => "star", "&" => "amp", + "-" => "minus", "_" => "underscore", "." => "dot", ".." => "dotdot", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index bf592c1f7be87..118b9ad631bfa 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -375,6 +375,68 @@ pub mod ops { type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output; } + + mod impls { + use crate::marker::Tuple; + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const Fn for &F + where + F: ~const Fn, + { + extern "rust-call" fn call(&self, args: A) -> F::Output { + (**self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnMut for &F + where + F: ~const Fn, + { + extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { + (**self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnOnce for &F + where + F: ~const Fn, + { + type Output = F::Output; + + extern "rust-call" fn call_once(self, args: A) -> F::Output { + (*self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnMut for &mut F + where + F: ~const FnMut, + { + extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { + (*self).call_mut(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnOnce for &mut F + where + F: ~const FnMut, + { + type Output = F::Output; + extern "rust-call" fn call_once(self, args: A) -> F::Output { + (*self).call_mut(args) + } + } + } } pub use self::function::{Fn, FnMut, FnOnce}; // endregion:fn From 9ad83deeccd8cb3d375b5558eb7e3d339b1a4e0b Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 17 Mar 2023 19:10:25 +0330 Subject: [PATCH 015/806] Support overloaded index MIR lowering --- crates/hir-ty/src/consteval/tests.rs | 46 +++++++++++++- crates/hir-ty/src/infer/mutability.rs | 15 +++++ crates/hir-ty/src/mir/lower/as_place.rs | 63 ++++++++++++++++++- .../src/handlers/mutability_errors.rs | 48 ++++++++++++++ 4 files changed, 169 insertions(+), 3 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index a658bfa0c9a83..944912b6c262f 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2,7 +2,8 @@ use base_db::fixture::WithFixture; use hir_def::db::DefDatabase; use crate::{ - consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner, + consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar, + Interner, }; use super::{ @@ -30,7 +31,12 @@ fn check_number(ra_fixture: &str, answer: i128) { match &r.data(Interner).value { chalk_ir::ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(b, _) => { - assert_eq!(b, &answer.to_le_bytes()[0..b.len()]); + assert_eq!( + b, + &answer.to_le_bytes()[0..b.len()], + "Bytes differ. In decimal form: actual = {}, expected = {answer}", + i128::from_le_bytes(pad16(b, true)) + ); } x => panic!("Expected number but found {:?}", x), }, @@ -215,6 +221,42 @@ fn overloaded_deref_autoref() { ); } +#[test] +fn overloaded_index() { + check_number( + r#" + //- minicore: index + struct Foo; + + impl core::ops::Index for Foo { + type Output = i32; + fn index(&self, index: usize) -> &i32 { + if index == 7 { + &700 + } else { + &1000 + } + } + } + + impl core::ops::IndexMut for Foo { + fn index_mut(&mut self, index: usize) -> &mut i32 { + if index == 7 { + &mut 7 + } else { + &mut 10 + } + } + } + + const GOAL: i32 = { + (Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7]) + }; + "#, + 3417, + ); +} + #[test] fn function_call() { check_number( diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 8e3d71788f2f0..784725da93508 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -95,6 +95,21 @@ impl<'a> InferenceContext<'a> { self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread)) } &Expr::Index { base, index } => { + if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { + if mutability == Mutability::Mut { + if let Some(index_trait) = self + .db + .lang_item(self.table.trait_env.krate, LangItem::IndexMut) + .and_then(|l| l.as_trait()) + { + if let Some(index_fn) = + self.db.trait_data(index_trait).method_by_name(&name![index_mut]) + { + *f = index_fn; + } + } + } + } self.infer_mut_expr(base, mutability); self.infer_mut_expr(index, Mutability::Not); } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index c6f4f66ada0e3..425904850ba85 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,6 +1,7 @@ //! MIR lowering for places use super::*; +use hir_def::FunctionId; use hir_expand::name; macro_rules! not_supported { @@ -193,7 +194,24 @@ impl MirLowerCtx<'_> { if index_ty != TyBuilder::usize() || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..)) { - not_supported!("overloaded index"); + let Some(index_fn) = self.infer.method_resolution(expr_id) else { + return Err(MirLowerError::UnresolvedMethod); + }; + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else { + return Ok(None); + }; + return self.lower_overloaded_index( + current, + base_place, + self.expr_ty_after_adjustments(*base), + self.expr_ty(expr_id), + index_operand, + expr_id.into(), + index_fn, + ); } let Some((mut p_base, current)) = self.lower_expr_as_place(current, *base, true)? else { @@ -210,6 +228,49 @@ impl MirLowerCtx<'_> { } } + fn lower_overloaded_index( + &mut self, + current: BasicBlockId, + place: Place, + base_ty: Ty, + result_ty: Ty, + index_operand: Operand, + span: MirSpan, + index_fn: (FunctionId, Substitution), + ) -> Result> { + let is_mutable = 'b: { + if let Some(index_mut_trait) = self.resolve_lang_item(LangItem::IndexMut)?.as_trait() { + if let Some(index_mut_fn) = + self.db.trait_data(index_mut_trait).method_by_name(&name![index_mut]) + { + break 'b index_mut_fn == index_fn.0; + } + } + false + }; + let (mutability, borrow_kind) = match is_mutable { + true => (Mutability::Mut, BorrowKind::Mut { allow_two_phase_borrow: false }), + false => (Mutability::Not, BorrowKind::Shared), + }; + let base_ref = TyKind::Ref(mutability, static_lifetime(), base_ty).intern(Interner); + let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner); + let ref_place: Place = self.temp(base_ref)?.into(); + self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span); + let mut result: Place = self.temp(result_ref)?.into(); + let index_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(), + index_fn.1, + ) + .intern(Interner), + ); + let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else { + return Ok(None); + }; + result.projection.push(ProjectionElem::Deref); + Ok(Some((result, current))) + } + fn lower_overloaded_deref( &mut self, current: BasicBlockId, diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 83c61f73db637..17a70f5701bc9 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -564,6 +564,54 @@ fn f(x: [(i32, u8); 10]) { ); } + #[test] + fn overloaded_index() { + check_diagnostics( + r#" +//- minicore: index +use core::ops::{Index, IndexMut}; + +struct Foo; +impl Index for Foo { + type Output = (i32, u8); + fn index(&self, index: usize) -> &(i32, u8) { + &(5, 2) + } +} +impl IndexMut for Foo { + fn index_mut(&mut self, index: usize) -> &mut (i32, u8) { + &mut (5, 2) + } +} +fn f() { + let mut x = Foo; + //^^^^^ 💡 weak: variable does not need to be mutable + let y = &x[2]; + let x = Foo; + let y = &mut x[2]; + //^^^^ 💡 error: cannot mutate immutable variable `x` + let mut x = &mut Foo; + //^^^^^ 💡 weak: variable does not need to be mutable + let y: &mut (i32, u8) = &mut x[2]; + let x = Foo; + let ref mut y = x[7]; + //^^^^ 💡 error: cannot mutate immutable variable `x` + let (ref mut y, _) = x[3]; + //^^^^ 💡 error: cannot mutate immutable variable `x` + match x[10] { + //^^^^^ 💡 error: cannot mutate immutable variable `x` + (ref y, _) => (), + (_, ref mut y) => (), + } + let mut x = Foo; + let mut i = 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let y = &mut x[i]; +} +"#, + ); + } + #[test] fn overloaded_deref() { check_diagnostics( From 453ae2e00e2f3bcd96a2fa78626296e14641dae8 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 18 Mar 2023 02:06:36 +0330 Subject: [PATCH 016/806] Support range MIR lowering --- crates/hir-ty/src/consteval/tests.rs | 12 +++++++ crates/hir-ty/src/infer/unify.rs | 9 ++++-- crates/hir-ty/src/mir/lower.rs | 48 ++++++++++++++++++++++++++-- crates/hir-ty/src/traits.rs | 4 +-- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 944912b6c262f..97c8d62860c64 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -561,6 +561,18 @@ fn for_loops() { ); } +#[test] +fn ranges() { + check_number( + r#" + //- minicore: range + const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end + + (10000..).start + (..100000).end + (..=1000000).end; + "#, + 1111111, + ); +} + #[test] fn recursion() { check_number( diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 2a07dd708c2a5..0e516b9399a68 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -633,7 +633,10 @@ impl<'a> InferenceTable<'a> { ) -> Option<(Option, Vec, Ty)> { match ty.callable_sig(self.db) { Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), - None => self.callable_sig_from_fn_trait(ty, num_args), + None => { + let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?; + Some((Some(f), args_ty, return_ty)) + } } } @@ -641,7 +644,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option, Vec, Ty)> { + ) -> Option<(FnTrait, Vec, Ty)> { let krate = self.trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let trait_data = self.db.trait_data(fn_once_trait); @@ -693,7 +696,7 @@ impl<'a> InferenceTable<'a> { }; let canonical = self.canonicalize(obligation.clone()); if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { - return Some((Some(fn_x), arg_tys, return_ty)); + return Some((fn_x, arg_tys, return_ty)); } } unreachable!("It should at least implement FnOnce at this point"); diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 04175fb4d9569..4b43e44a8ec31 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -14,7 +14,7 @@ use hir_def::{ layout::LayoutError, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, + AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -643,7 +643,7 @@ impl MirLowerCtx<'_> { }, } }).collect(), - None => operands.into_iter().map(|x| x).collect::>().ok_or( + None => operands.into_iter().collect::>().ok_or( MirLowerError::TypeError("missing field in record literal"), )?, }, @@ -761,7 +761,49 @@ impl MirLowerCtx<'_> { ); Ok(Some(current)) } - Expr::Range { .. } => not_supported!("range"), + &Expr::Range { lhs, rhs, range_type: _ } => { + let ty = self.expr_ty(expr_id); + let Some((adt, subst)) = ty.as_adt() else { + return Err(MirLowerError::TypeError("Range type is not adt")); + }; + let AdtId::StructId(st) = adt else { + return Err(MirLowerError::TypeError("Range type is not struct")); + }; + let mut lp = None; + let mut rp = None; + if let Some(x) = lhs { + let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + return Ok(None); + }; + lp = Some(o); + current = c; + } + if let Some(x) = rhs { + let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + return Ok(None); + }; + rp = Some(o); + current = c; + } + self.push_assignment( + current, + place, + Rvalue::Aggregate( + AggregateKind::Adt(st.into(), subst.clone()), + self.db.struct_data(st).variant_data.fields().iter().map(|x| { + let o = match x.1.name.as_str() { + Some("start") => lp.take(), + Some("end") => rp.take(), + Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())), + _ => None, + }; + o.ok_or(MirLowerError::UnresolvedField) + }).collect::>()?, + ), + expr_id.into(), + ); + Ok(Some(current)) + }, Expr::Closure { .. } => not_supported!("closure"), Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index aebf59f31525a..e7fffc4cc7d74 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -188,7 +188,7 @@ impl FnTrait { } } - pub fn method_name(&self) -> Name { + pub fn method_name(self) -> Name { match self { FnTrait::FnOnce => name!(call_once), FnTrait::FnMut => name!(call_mut), @@ -196,7 +196,7 @@ impl FnTrait { } } - pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option { + pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option { let target = db.lang_item(krate, self.lang_item())?; match target { LangItemTarget::Trait(t) => Some(t), From 8e73ea52537fe5189fac5cd02380592563fe7f0c Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 19 Mar 2023 13:02:51 +0330 Subject: [PATCH 017/806] Desugar try blocks --- crates/hir-def/src/body/lower.rs | 87 +++++++++++++++++++++++---- crates/hir-def/src/body/pretty.rs | 3 - crates/hir-def/src/body/scope.rs | 3 +- crates/hir-def/src/expr.rs | 6 -- crates/hir-expand/src/name.rs | 14 ++++- crates/hir-ty/src/consteval/tests.rs | 54 +++++++++++++++++ crates/hir-ty/src/infer.rs | 4 -- crates/hir-ty/src/infer/expr.rs | 20 ------ crates/hir-ty/src/infer/mutability.rs | 1 - crates/hir-ty/src/mir/lower.rs | 87 ++++++++++++++++++--------- crates/hir-ty/src/tests/traits.rs | 20 ++++-- 11 files changed, 215 insertions(+), 84 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 448821d3844ab..a93fcb3b1dc29 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -19,7 +19,7 @@ use rustc_hash::FxHashMap; use smallvec::SmallVec; use syntax::{ ast::{ - self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind, + self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind, SlicePatComponents, }, AstNode, AstPtr, SyntaxNodePtr, @@ -100,6 +100,7 @@ pub(super) fn lower( _c: Count::new(), }, expander, + current_try_block: None, is_lowering_assignee_expr: false, is_lowering_generator: false, } @@ -113,6 +114,7 @@ struct ExprCollector<'a> { body: Body, krate: CrateId, source_map: BodySourceMap, + current_try_block: Option, is_lowering_assignee_expr: bool, is_lowering_generator: bool, } @@ -222,6 +224,10 @@ impl ExprCollector<'_> { self.source_map.label_map.insert(src, id); id } + // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow. + fn alloc_label_desugared(&mut self, label: Label) -> LabelId { + self.body.labels.alloc(label) + } fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId { let id = self.body.labels.alloc(label); self.source_map.label_map_back.insert(id, src); @@ -259,13 +265,7 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) } ast::Expr::BlockExpr(e) => match e.modifier() { - Some(ast::BlockModifier::Try(_)) => { - self.collect_block_(e, |id, statements, tail| Expr::TryBlock { - id, - statements, - tail, - }) - } + Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e), Some(ast::BlockModifier::Unsafe(_)) => { self.collect_block_(e, |id, statements, tail| Expr::Unsafe { id, @@ -606,6 +606,59 @@ impl ExprCollector<'_> { }) } + /// Desugar `try { ; }` into `': { ; ::std::ops::Try::from_output() }`, + /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` + /// and save the `` to use it as a break target for desugaring of the `?` operator. + fn collect_try_block(&mut self, e: BlockExpr) -> ExprId { + let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else { + return self.alloc_expr_desugared(Expr::Missing); + }; + let prev_try_block = self.current_try_block.take(); + self.current_try_block = + Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() })); + let expr_id = self.collect_block(e); + let callee = self.alloc_expr_desugared(Expr::Path(try_from_output)); + let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else { + unreachable!("It is the output of collect block"); + }; + *label = self.current_try_block; + let next_tail = match *tail { + Some(tail) => self.alloc_expr_desugared(Expr::Call { + callee, + args: Box::new([tail]), + is_assignee_expr: false, + }), + None => { + let unit = self.alloc_expr_desugared(Expr::Tuple { + exprs: Box::new([]), + is_assignee_expr: false, + }); + self.alloc_expr_desugared(Expr::Call { + callee, + args: Box::new([unit]), + is_assignee_expr: false, + }) + } + }; + let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { + unreachable!("It is the output of collect block"); + }; + *tail = Some(next_tail); + self.current_try_block = prev_try_block; + expr_id + } + + /// Desugar `ast::TryExpr` from: `?` into: + /// ```ignore (pseudo-rust) + /// match Try::branch() { + /// ControlFlow::Continue(val) => val, + /// ControlFlow::Break(residual) => + /// // If there is an enclosing `try {...}`: + /// break 'catch_target Try::from_residual(residual), + /// // Otherwise: + /// return Try::from_residual(residual), + /// } + /// ``` fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: { if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) { @@ -628,7 +681,9 @@ impl ExprCollector<'_> { Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false }, syntax_ptr.clone(), ); - let continue_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated); + let continue_name = Name::generate_new_name(); + let continue_binding = + self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated); let continue_bpat = self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None }); self.add_definition_to_binding(continue_binding, continue_bpat); @@ -639,9 +694,10 @@ impl ExprCollector<'_> { ellipsis: None, }), guard: None, - expr: self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()), + expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()), }; - let break_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated); + let break_name = Name::generate_new_name(); + let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated); let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); self.add_definition_to_binding(break_binding, break_bpat); let break_arm = MatchArm { @@ -652,13 +708,18 @@ impl ExprCollector<'_> { }), guard: None, expr: { - let x = self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()); + let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); let result = self.alloc_expr( Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, syntax_ptr.clone(), ); - self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone()) + if let Some(label) = self.current_try_block { + let label = Some(self.body.labels[label].name.clone()); + self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone()) + } else { + self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone()) + } }, }; let arms = Box::new([continue_arm, break_arm]); diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index c091ad0d150f6..8c9d77620e188 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -420,9 +420,6 @@ impl<'a> Printer<'a> { Expr::Unsafe { id: _, statements, tail } => { self.print_block(Some("unsafe "), statements, tail); } - Expr::TryBlock { id: _, statements, tail } => { - self.print_block(Some("try "), statements, tail); - } Expr::Async { id: _, statements, tail } => { self.print_block(Some("async "), statements, tail); } diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 12fc1f116d7da..8ddb89a4725d5 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -202,8 +202,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope } Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } - | Expr::Const { id, statements, tail } - | Expr::TryBlock { id, statements, tail } => { + | Expr::Const { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index 8b1528f81e61e..7ede19cc3c2b4 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -122,11 +122,6 @@ pub enum Expr { tail: Option, label: Option, }, - TryBlock { - id: BlockId, - statements: Box<[Statement]>, - tail: Option, - }, Async { id: BlockId, statements: Box<[Statement]>, @@ -310,7 +305,6 @@ impl Expr { f(*expr); } Expr::Block { statements, tail, .. } - | Expr::TryBlock { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } | Expr::Async { statements, tail, .. } | Expr::Const { statements, tail, .. } => { diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 71eb35d9df8e4..8099c20b027c2 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -78,7 +78,7 @@ impl Name { Self::new_text(lt.text().into()) } - /// Shortcut to create inline plain text name + /// Shortcut to create inline plain text name. Panics if `text.len() > 22` const fn new_inline(text: &str) -> Name { Name::new_text(SmolStr::new_inline(text)) } @@ -112,6 +112,18 @@ impl Name { Name::new_inline("[missing name]") } + /// Generates a new name which is only equal to itself, by incrementing a counter. Due + /// its implementation, it should not be used in things that salsa considers, like + /// type names or field names, and it should be only used in names of local variables + /// and labels and similar things. + pub fn generate_new_name() -> Name { + use std::sync::atomic::{AtomicUsize, Ordering}; + static CNT: AtomicUsize = AtomicUsize::new(0); + let c = CNT.fetch_add(1, Ordering::Relaxed); + // FIXME: Currently a `__RA_generated_name` in user code will break our analysis + Name::new_text(format!("__RA_geneated_name_{c}").into()) + } + /// Returns the tuple index this name represents if it is a tuple field. pub fn as_tuple_index(&self) -> Option { match self.0 { diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 97c8d62860c64..2ba0cbd5db4f0 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -522,6 +522,42 @@ fn loops() { "#, 4, ); + check_number( + r#" + const GOAL: u8 = { + let mut x = 0; + loop { + x = x + 1; + if x == 5 { + break x + 2; + } + } + }; + "#, + 7, + ); + check_number( + r#" + const GOAL: u8 = { + 'a: loop { + let x = 'b: loop { + let x = 'c: loop { + let x = 'd: loop { + let x = 'e: loop { + break 'd 1; + }; + break 2 + x; + }; + break 3 + x; + }; + break 'a 4 + x; + }; + break 5 + x; + } + }; + "#, + 8, + ); } #[test] @@ -1019,6 +1055,24 @@ fn try_operator() { ); } +#[test] +fn try_block() { + check_number( + r#" + //- minicore: option, try + const fn g(x: Option, y: Option) -> i32 { + let r = try { x? * y? }; + match r { + Some(k) => k, + None => 5, + } + } + const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None); + "#, + 215, + ); +} + #[test] fn or_pattern() { check_number( diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index d1b9aff36d242..38b7dee75fd5b 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1025,10 +1025,6 @@ impl<'a> InferenceContext<'a> { self.resolve_lang_item(lang)?.as_trait() } - fn resolve_ops_try_output(&self) -> Option { - self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?) - } - fn resolve_ops_neg_output(&self) -> Option { self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?) } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 82119c97ec215..6d2aa59ea3598 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -159,26 +159,6 @@ impl<'a> InferenceContext<'a> { }) .1 } - Expr::TryBlock { id: _, statements, tail } => { - // The type that is returned from the try block - let try_ty = self.table.new_type_var(); - if let Some(ty) = expected.only_has_type(&mut self.table) { - self.unify(&try_ty, &ty); - } - - // The ok-ish type that is expected from the last expression - let ok_ty = - self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output()); - - self.infer_block( - tgt_expr, - statements, - *tail, - None, - &Expectation::has_type(ok_ty.clone()), - ); - try_ty - } Expr::Async { id: _, statements, tail } => { let ret_ty = self.table.new_type_var(); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 784725da93508..7ed21d230c7f6 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -44,7 +44,6 @@ impl<'a> InferenceContext<'a> { } Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), Expr::Block { id: _, statements, tail, label: _ } - | Expr::TryBlock { id: _, statements, tail } | Expr::Async { id: _, statements, tail } | Expr::Const { id: _, statements, tail } | Expr::Unsafe { id: _, statements, tail } => { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4b43e44a8ec31..5d9ae320726ff 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -18,6 +18,7 @@ use hir_def::{ }; use hir_expand::name::Name; use la_arena::ArenaMap; +use rustc_hash::FxHashMap; use crate::{ consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, @@ -32,17 +33,21 @@ mod pattern_matching; use pattern_matching::AdtPatternShape; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] struct LoopBlocks { begin: BasicBlockId, /// `None` for loops that are not terminating end: Option, + place: Place, } struct MirLowerCtx<'a> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, + // FIXME: we should resolve labels in HIR lowering and always work with label id here, not + // with raw names. + labeled_loop_blocks: FxHashMap, discr_temp: Option, db: &'a dyn HirDatabase, body: &'a Body, @@ -72,6 +77,7 @@ pub enum MirLowerError { ImplementationError(&'static str), LangItemNotFound(LangItem), MutatingRvalue, + UnresolvedLabel, } macro_rules! not_supported { @@ -375,19 +381,29 @@ impl MirLowerCtx<'_> { Ok(self.merge_blocks(Some(then_target), else_target)) } Expr::Unsafe { id: _, statements, tail } => { - self.lower_block_to_place(None, statements, current, *tail, place) + self.lower_block_to_place(statements, current, *tail, place) } Expr::Block { id: _, statements, tail, label } => { - self.lower_block_to_place(*label, statements, current, *tail, place) + if let Some(label) = label { + self.lower_loop(current, place.clone(), Some(*label), |this, begin| { + if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? { + let end = this.current_loop_end()?; + this.set_goto(block, end); + } + Ok(()) + }) + } else { + self.lower_block_to_place(statements, current, *tail, place) + } } - Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| { + Expr::Loop { body, label } => self.lower_loop(current, place, *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| { + self.lower_loop(current, place, *label, |this, begin| { let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { return Ok(()); }; @@ -438,7 +454,7 @@ impl MirLowerCtx<'_> { 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| { + self.lower_loop(current, place, 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(()); @@ -558,24 +574,28 @@ impl MirLowerCtx<'_> { Some(_) => not_supported!("continue with label"), None => { let loop_data = - self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?; + self.current_loop_blocks.as_ref().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) - } + if let Some(expr) = expr { + let loop_data = match label { + Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, + None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?, + }; + let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else { + return Ok(None); + }; + current = c; } + let end = match label { + Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"), + None => self.current_loop_end()?, + }; + self.set_goto(current, end); + Ok(None) } Expr::Return { expr } => { if let Some(expr) = expr { @@ -668,7 +688,6 @@ impl MirLowerCtx<'_> { } Expr::Await { .. } => not_supported!("await"), 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: _ } => { @@ -1085,19 +1104,34 @@ impl MirLowerCtx<'_> { fn lower_loop( &mut self, prev_block: BasicBlockId, + place: Place, 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 })); + let prev = mem::replace( + &mut self.current_loop_blocks, + Some(LoopBlocks { begin, end: None, place }), + ); + let prev_label = if let Some(label) = label { + // We should generate the end now, to make sure that it wouldn't change later. It is + // bad as we may emit end (unneccessary unreachable block) for unterminating loop, but + // it should not affect correctness. + self.current_loop_end()?; + self.labeled_loop_blocks.insert( + self.body.labels[label].name.clone(), + self.current_loop_blocks.as_ref().unwrap().clone(), + ) + } else { + 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"))?; + if let Some(prev) = prev_label { + self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev); + } Ok(my.end) } @@ -1185,15 +1219,11 @@ impl MirLowerCtx<'_> { 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: _ } => { @@ -1355,6 +1385,7 @@ pub fn lower_to_mir( body, owner, current_loop_blocks: None, + labeled_loop_blocks: Default::default(), discr_temp: None, }; let mut current = start_block; diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index da76d7fd83f76..97ec1bb871d4b 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -206,19 +206,27 @@ fn test() { fn infer_try_trait() { check_types( r#" -//- minicore: try, result +//- minicore: try, result, from fn test() { let r: Result = Result::Ok(1); let v = r?; v; } //^ i32 - -impl core::ops::Try for Result { - type Output = O; - type Error = Result; +"#, + ); } -impl> core::ops::FromResidual> for Result {} +#[test] +fn infer_try_block() { + // FIXME: We should test more cases, but it currently doesn't work, since + // our labeled block type inference is broken. + check_types( + r#" +//- minicore: try, option +fn test() { + let x: Option<_> = try { Some(2)?; }; + //^ Option<()> +} "#, ); } From dbf04a5ee29101afbd1db665369bb1d21224efb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 20 Mar 2023 08:31:01 +0200 Subject: [PATCH 018/806] :arrow_up: rust-analyzer --- Cargo.lock | 16 +- bench_data/numerous_macro_rules | 4 +- crates/hir-def/src/adt.rs | 19 +- crates/hir-def/src/body.rs | 42 +- crates/hir-def/src/body/lower.rs | 2 + crates/hir-def/src/body/pretty.rs | 10 +- crates/hir-def/src/data.rs | 33 +- crates/hir-def/src/db.rs | 4 +- crates/hir-def/src/expr.rs | 1 + crates/hir-def/src/macro_expansion_tests.rs | 2 +- crates/hir-def/src/nameres.rs | 14 +- crates/hir-def/src/nameres/collector.rs | 12 +- crates/hir-def/src/test_db.rs | 8 +- crates/hir-def/src/visibility.rs | 28 +- crates/hir-expand/src/attrs.rs | 12 +- crates/hir-expand/src/builtin_attr_macro.rs | 8 +- crates/hir-expand/src/builtin_derive_macro.rs | 28 +- crates/hir-expand/src/builtin_fn_macro.rs | 56 +-- crates/hir-expand/src/db.rs | 51 +-- crates/hir-expand/src/eager.rs | 8 +- crates/hir-expand/src/fixup.rs | 3 +- crates/hir-expand/src/hygiene.rs | 18 +- crates/hir-expand/src/lib.rs | 64 +-- crates/hir-expand/src/mod_path.rs | 10 +- crates/hir-expand/src/proc_macro.rs | 19 +- crates/hir-ty/Cargo.toml | 8 +- crates/hir-ty/src/chalk_ext.rs | 15 +- crates/hir-ty/src/diagnostics.rs | 6 + crates/hir-ty/src/infer/expr.rs | 73 ++-- crates/hir-ty/src/infer/pat.rs | 37 +- crates/hir-ty/src/method_resolution.rs | 92 ++++- crates/hir-ty/src/mir/lower.rs | 20 +- crates/hir-ty/src/test_db.rs | 8 +- crates/hir-ty/src/tests.rs | 2 +- crates/hir-ty/src/tests/method_resolution.rs | 11 +- crates/hir-ty/src/tests/regression.rs | 32 ++ crates/hir-ty/src/tests/simple.rs | 60 +-- crates/hir-ty/src/tests/traits.rs | 71 +++- crates/hir/src/db.rs | 2 +- crates/hir/src/diagnostics.rs | 5 +- crates/hir/src/lib.rs | 35 +- crates/hir/src/semantics.rs | 8 +- crates/hir/src/source_analyzer.rs | 7 +- .../src/handlers/generate_function.rs | 2 +- .../ide-assists/src/handlers/inline_call.rs | 40 +- crates/ide-assists/src/handlers/remove_dbg.rs | 2 +- crates/ide-assists/src/lib.rs | 5 +- crates/ide-completion/src/completions.rs | 19 +- crates/ide-completion/src/completions/dot.rs | 6 +- crates/ide-completion/src/context.rs | 2 + crates/ide-completion/src/context/analysis.rs | 62 ++- crates/ide-completion/src/context/tests.rs | 12 + crates/ide-completion/src/render/pattern.rs | 26 +- crates/ide-completion/src/tests/pattern.rs | 1 + crates/ide-completion/src/tests/record.rs | 60 +++ crates/ide-completion/src/tests/special.rs | 1 + crates/ide-db/src/apply_change.rs | 2 +- crates/ide-db/src/defs.rs | 9 +- crates/ide-db/src/lib.rs | 8 +- .../src/handlers/incoherent_impl.rs | 77 ++++ .../src/handlers/incorrect_case.rs | 2 +- .../src/handlers/missing_fields.rs | 2 +- .../src/handlers/missing_unsafe.rs | 381 +++++++++++++++++- .../src/handlers/mutability_errors.rs | 24 ++ .../src/handlers/no_such_field.rs | 2 +- .../src/handlers/private_field.rs | 20 + .../replace_filter_map_next_with_find_map.rs | 2 +- .../src/handlers/type_mismatch.rs | 2 +- .../src/handlers/unresolved_field.rs | 2 +- .../src/handlers/unresolved_method.rs | 2 +- .../src/handlers/unresolved_module.rs | 2 +- crates/ide-diagnostics/src/lib.rs | 2 + crates/ide/src/goto_implementation.rs | 1 + crates/ide/src/goto_type_definition.rs | 2 +- crates/ide/src/inlay_hints/adjustment.rs | 46 ++- crates/ide/src/inlay_hints/chaining.rs | 12 +- crates/ide/src/signature_help.rs | 263 +++++++++++- crates/parser/src/grammar/patterns.rs | 15 +- crates/project-model/src/build_scripts.rs | 5 +- crates/project-model/src/cargo_workspace.rs | 6 +- crates/project-model/src/lib.rs | 2 +- crates/project-model/src/tests.rs | 6 +- crates/project-model/src/workspace.rs | 224 +++++----- .../rust-analyzer/src/cli/analysis_stats.rs | 6 +- crates/rust-analyzer/src/cli/diagnostics.rs | 4 +- crates/rust-analyzer/src/cli/lsif.rs | 4 +- crates/rust-analyzer/src/cli/scip.rs | 4 +- crates/rust-analyzer/src/cli/ssr.rs | 4 +- crates/rust-analyzer/src/config.rs | 20 +- crates/rust-analyzer/src/dispatch.rs | 36 ++ crates/rust-analyzer/src/handlers.rs | 12 +- crates/rust-analyzer/src/lsp_utils.rs | 40 +- crates/rust-analyzer/src/main_loop.rs | 19 +- crates/rust-analyzer/src/reload.rs | 41 +- crates/syntax/src/ast/expr_ext.rs | 43 +- crates/syntax/src/ast/node_ext.rs | 6 - crates/test-utils/src/minicore.rs | 2 + editors/code/package.json | 16 + editors/code/src/client.ts | 13 +- editors/code/src/commands.ts | 29 +- editors/code/src/config.ts | 29 +- editors/code/src/ctx.ts | 69 +++- editors/code/src/lsp_ext.ts | 1 + editors/code/src/main.ts | 1 + editors/code/src/rust_project.ts | 91 +++++ editors/code/src/util.ts | 17 + 106 files changed, 2220 insertions(+), 610 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/incoherent_impl.rs create mode 100644 editors/code/src/rust_project.ts diff --git a/Cargo.lock b/Cargo.lock index fc77515b63bfd..25242c6028a47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.88.0" +version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df80a3fbc1f0e59f560eeeebca94bf655566a8ad3023c210a109deb6056455a" +checksum = "ea176c50987dc4765961aa165001e8eb5a722a26308c5797a47303ea91686aab" dependencies = [ "proc-macro2", "quote", @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.88.0" +version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39e5272016916956298cceea5147006f897972c274a768ed4d6e074efe5d3fb" +checksum = "473b480241695428c14e8f84f1c9a47ef232450a50faf3a4041e5c9dc11e0a3b" dependencies = [ "bitflags", "chalk-derive", @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.88.0" +version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d60b42ad7478d3e027e2f9ea4e99fbbb8fdee0c8c3cf068be269f57e603618" +checksum = "6764b4fe67cac3a3758185084efbfbd39bf0352795824ba849ddd2b64cd4bb28" dependencies = [ "chalk-derive", "chalk-ir", @@ -205,9 +205,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.88.0" +version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab30620ea5b36819525eaab2204f4b8e1842fc7ee36826424a28bef59ae7fecf" +checksum = "55a7e6160966eceb6e7dcc2f479a2af4c477aaf5bccbc640d82515995ab1a6cc" dependencies = [ "chalk-derive", "chalk-ir", diff --git a/bench_data/numerous_macro_rules b/bench_data/numerous_macro_rules index bf89ed594f707..7610a3ae1e3cb 100644 --- a/bench_data/numerous_macro_rules +++ b/bench_data/numerous_macro_rules @@ -341,8 +341,8 @@ macro_rules! __ra_macro_fixture339 {($name : ident )=>{ impl Clone for $name macro_rules! __ra_macro_fixture340 {([$($stack : tt )*])=>{$($stack )* }; ([$($stack : tt )*]{$($tail : tt )* })=>{$($stack )* { remove_sections_inner ! ([]$($tail )*); }}; ([$($stack : tt )*]$t : tt $($tail : tt )*)=>{ remove_sections ! ([$($stack )* $t ]$($tail )*); }; } macro_rules! __ra_macro_fixture341 {($t : ty ,$z : expr )=>{ impl Zero for $t { fn zero ()-> Self {$z as $t } fn is_zero (& self )-> bool { self == & Self :: zero ()}}}; } macro_rules! __ra_macro_fixture342 {($($ident : ident ),* $(,)?)=>{$(# [ allow ( bad_style )] pub const $ident : super :: Name = super :: Name :: new_inline ( stringify ! ($ident )); )* }; } -macro_rules! __ra_macro_fixture343 {($($trait : ident =>$expand : ident ),* )=>{# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum BuiltinDeriveExpander {$($trait ),* } impl BuiltinDeriveExpander { pub fn expand (& self , db : & dyn AstDatabase , id : LazyMacroId , tt : & tt :: Subtree , )-> Result < tt :: Subtree , mbe :: ExpandError > { let expander = match * self {$(BuiltinDeriveExpander ::$trait =>$expand , )* }; expander ( db , id , tt )} fn find_by_name ( name : & name :: Name )-> Option < Self > { match name {$(id if id == & name :: name ! [$trait ]=> Some ( BuiltinDeriveExpander ::$trait ), )* _ => None , }}}}; } -macro_rules! __ra_macro_fixture344 {( LAZY : $(($name : ident , $kind : ident )=>$expand : ident ),* , EAGER : $(($e_name : ident , $e_kind : ident )=>$e_expand : ident ),* )=>{# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum BuiltinFnLikeExpander {$($kind ),* }# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum EagerExpander {$($e_kind ),* } impl BuiltinFnLikeExpander { pub fn expand (& self , db : & dyn AstDatabase , id : LazyMacroId , tt : & tt :: Subtree , )-> ExpandResult < tt :: Subtree > { let expander = match * self {$(BuiltinFnLikeExpander ::$kind =>$expand , )* }; expander ( db , id , tt )}} impl EagerExpander { pub fn expand (& self , db : & dyn AstDatabase , arg_id : EagerMacroId , tt : & tt :: Subtree , )-> ExpandResult < Option < ( tt :: Subtree , FragmentKind )>> { let expander = match * self {$(EagerExpander ::$e_kind =>$e_expand , )* }; expander ( db , arg_id , tt )}} fn find_by_name ( ident : & name :: Name )-> Option < Either < BuiltinFnLikeExpander , EagerExpander >> { match ident {$(id if id == & name :: name ! [$name ]=> Some ( Either :: Left ( BuiltinFnLikeExpander ::$kind )), )* $(id if id == & name :: name ! [$e_name ]=> Some ( Either :: Right ( EagerExpander ::$e_kind )), )* _ => return None , }}}; } +macro_rules! __ra_macro_fixture343 {($($trait : ident =>$expand : ident ),* )=>{# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum BuiltinDeriveExpander {$($trait ),* } impl BuiltinDeriveExpander { pub fn expand (& self , db : & dyn ExpandDatabase , id : LazyMacroId , tt : & tt :: Subtree , )-> Result < tt :: Subtree , mbe :: ExpandError > { let expander = match * self {$(BuiltinDeriveExpander ::$trait =>$expand , )* }; expander ( db , id , tt )} fn find_by_name ( name : & name :: Name )-> Option < Self > { match name {$(id if id == & name :: name ! [$trait ]=> Some ( BuiltinDeriveExpander ::$trait ), )* _ => None , }}}}; } +macro_rules! __ra_macro_fixture344 {( LAZY : $(($name : ident , $kind : ident )=>$expand : ident ),* , EAGER : $(($e_name : ident , $e_kind : ident )=>$e_expand : ident ),* )=>{# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum BuiltinFnLikeExpander {$($kind ),* }# [ derive ( Debug , Clone , Copy , PartialEq , Eq , Hash )] pub enum EagerExpander {$($e_kind ),* } impl BuiltinFnLikeExpander { pub fn expand (& self , db : & dyn ExpandDatabase , id : LazyMacroId , tt : & tt :: Subtree , )-> ExpandResult < tt :: Subtree > { let expander = match * self {$(BuiltinFnLikeExpander ::$kind =>$expand , )* }; expander ( db , id , tt )}} impl EagerExpander { pub fn expand (& self , db : & dyn ExpandDatabase , arg_id : EagerMacroId , tt : & tt :: Subtree , )-> ExpandResult < Option < ( tt :: Subtree , FragmentKind )>> { let expander = match * self {$(EagerExpander ::$e_kind =>$e_expand , )* }; expander ( db , arg_id , tt )}} fn find_by_name ( ident : & name :: Name )-> Option < Either < BuiltinFnLikeExpander , EagerExpander >> { match ident {$(id if id == & name :: name ! [$name ]=> Some ( Either :: Left ( BuiltinFnLikeExpander ::$kind )), )* $(id if id == & name :: name ! [$e_name ]=> Some ( Either :: Right ( EagerExpander ::$e_kind )), )* _ => return None , }}}; } macro_rules! __ra_macro_fixture345 {($($ty : ty =>$this : ident $im : block );*)=>{$(impl ToTokenTree for $ty { fn to_token ($this )-> tt :: TokenTree { let leaf : tt :: Leaf = $im . into (); leaf . into ()}} impl ToTokenTree for &$ty { fn to_token ($this )-> tt :: TokenTree { let leaf : tt :: Leaf = $im . clone (). into (); leaf . into ()}})* }} macro_rules! __ra_macro_fixture346 {($name : ident )=>{ impl $crate :: salsa :: InternKey for $name { fn from_intern_id ( v : $crate :: salsa :: InternId )-> Self {$name ( v )} fn as_intern_id (& self )-> $crate :: salsa :: InternId { self . 0 }}}; } macro_rules! __ra_macro_fixture347 {($($var : ident ($t : ty )),+ )=>{$(impl From <$t > for AttrOwner { fn from ( t : $t )-> AttrOwner { AttrOwner ::$var ( t )}})+ }; } diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs index 9bc1c54a3c641..b336f59ffee31 100644 --- a/crates/hir-def/src/adt.rs +++ b/crates/hir-def/src/adt.rs @@ -40,6 +40,7 @@ pub struct StructData { pub repr: Option, pub visibility: RawVisibility, pub rustc_has_incoherent_inherent_impls: bool, + pub fundamental: bool, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -173,10 +174,10 @@ impl StructData { let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); - let rustc_has_incoherent_inherent_impls = item_tree - .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) - .by_key("rustc_has_incoherent_inherent_impls") - .exists(); + let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let rustc_has_incoherent_inherent_impls = + attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); + let fundamental = attrs.by_key("fundamental").exists(); let strukt = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -196,6 +197,7 @@ impl StructData { repr, visibility: item_tree[strukt.visibility].clone(), rustc_has_incoherent_inherent_impls, + fundamental, }), diagnostics.into(), ) @@ -215,10 +217,10 @@ impl StructData { let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); - let rustc_has_incoherent_inherent_impls = item_tree - .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) - .by_key("rustc_has_incoherent_inherent_impls") - .exists(); + let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let rustc_has_incoherent_inherent_impls = + attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); + let fundamental = attrs.by_key("fundamental").exists(); let union = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -238,6 +240,7 @@ impl StructData { repr, visibility: item_tree[union.visibility].clone(), rustc_has_incoherent_inherent_impls, + fundamental, }), diagnostics.into(), ) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 3be477d48774a..b70e658efd79c 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -24,7 +24,9 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; use crate::{ attr::Attrs, db::DefDatabase, - expr::{dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId}, + expr::{ + dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, + }, item_scope::BuiltinShadowMode, macro_id_to_def_id, nameres::DefMap, @@ -432,6 +434,44 @@ impl Body { pats.shrink_to_fit(); bindings.shrink_to_fit(); } + + pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { + self.walk_pats(pat_id, &mut |pat| { + if let Pat::Bind { id, .. } = pat { + f(*id); + } + }); + } + + pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) { + let pat = &self[pat_id]; + f(pat); + match pat { + Pat::Range { .. } + | Pat::Lit(..) + | Pat::Path(..) + | Pat::ConstBlock(..) + | Pat::Wild + | Pat::Missing => {} + &Pat::Bind { subpat, .. } => { + if let Some(subpat) = subpat { + self.walk_pats(subpat, f); + } + } + Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { + args.iter().copied().for_each(|p| self.walk_pats(p, f)); + } + Pat::Ref { pat, .. } => self.walk_pats(*pat, f), + Pat::Slice { prefix, slice, suffix } => { + let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); + total_iter.copied().for_each(|p| self.walk_pats(p, f)); + } + Pat::Record { args, .. } => { + args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f)); + } + Pat::Box { inner } => self.walk_pats(*inner, f), + } + } } impl Default for Body { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 83ce9b6acbba8..fedaf39559858 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -499,6 +499,8 @@ impl ExprCollector<'_> { Movability::Movable }; ClosureKind::Generator(movability) + } else if e.async_token().is_some() { + ClosureKind::Async } else { ClosureKind::Closure }; diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index f8b159797e44a..5a9b825a2530b 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -360,8 +360,14 @@ impl<'a> Printer<'a> { w!(self, "]"); } Expr::Closure { args, arg_types, ret_type, body, closure_kind } => { - if let ClosureKind::Generator(Movability::Static) = closure_kind { - w!(self, "static "); + match closure_kind { + ClosureKind::Generator(Movability::Static) => { + w!(self, "static "); + } + ClosureKind::Async => { + w!(self, "async "); + } + _ => (), } w!(self, "|"); for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index ee6e269fe5586..1633a33bedde8 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -35,6 +35,7 @@ pub struct FunctionData { pub visibility: RawVisibility, pub abi: Option>, pub legacy_const_generics_indices: Box<[u32]>, + pub rustc_allow_incoherent_impl: bool, flags: FnFlags, } @@ -84,13 +85,14 @@ impl FunctionData { } } - let legacy_const_generics_indices = item_tree - .attrs(db, krate, ModItem::from(loc.id.value).into()) + let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); + let legacy_const_generics_indices = attrs .by_key("rustc_legacy_const_generics") .tt_values() .next() .map(parse_rustc_legacy_const_generics) .unwrap_or_default(); + let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); Arc::new(FunctionData { name: func.name.clone(), @@ -108,6 +110,7 @@ impl FunctionData { abi: func.abi.clone(), legacy_const_generics_indices, flags, + rustc_allow_incoherent_impl, }) } @@ -171,6 +174,7 @@ pub struct TypeAliasData { pub visibility: RawVisibility, pub is_extern: bool, pub rustc_has_incoherent_inherent_impls: bool, + pub rustc_allow_incoherent_impl: bool, /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). pub bounds: Vec>, } @@ -189,10 +193,14 @@ impl TypeAliasData { item_tree[typ.visibility].clone() }; - let rustc_has_incoherent_inherent_impls = item_tree - .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into()) - .by_key("rustc_has_incoherent_inherent_impls") - .exists(); + let attrs = item_tree.attrs( + db, + loc.container.module(db).krate(), + ModItem::from(loc.id.value).into(), + ); + let rustc_has_incoherent_inherent_impls = + attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); + let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); Arc::new(TypeAliasData { name: typ.name.clone(), @@ -200,6 +208,7 @@ impl TypeAliasData { visibility, is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), rustc_has_incoherent_inherent_impls, + rustc_allow_incoherent_impl, bounds: typ.bounds.to_vec(), }) } @@ -212,11 +221,12 @@ pub struct TraitData { pub is_auto: bool, pub is_unsafe: bool, pub rustc_has_incoherent_inherent_impls: bool, + pub skip_array_during_method_dispatch: bool, + pub fundamental: bool, pub visibility: RawVisibility, /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore /// method calls to this trait's methods when the receiver is an array and the crate edition is /// 2015 or 2018. - pub skip_array_during_method_dispatch: bool, // box it as the vec is usually empty anyways pub attribute_calls: Option, MacroCallId)>>>, } @@ -245,6 +255,7 @@ impl TraitData { attrs.by_key("rustc_skip_array_during_method_dispatch").exists(); let rustc_has_incoherent_inherent_impls = attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); + let fundamental = attrs.by_key("fundamental").exists(); let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); @@ -260,6 +271,7 @@ impl TraitData { visibility, skip_array_during_method_dispatch, rustc_has_incoherent_inherent_impls, + fundamental, }), diagnostics.into(), ) @@ -450,6 +462,7 @@ pub struct ConstData { pub name: Option, pub type_ref: Interned, pub visibility: RawVisibility, + pub rustc_allow_incoherent_impl: bool, } impl ConstData { @@ -463,10 +476,16 @@ impl ConstData { item_tree[konst.visibility].clone() }; + let rustc_allow_incoherent_impl = item_tree + .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into()) + .by_key("rustc_allow_incoherent_impl") + .exists(); + Arc::new(ConstData { name: konst.name.clone(), type_ref: konst.type_ref.clone(), visibility, + rustc_allow_incoherent_impl, }) } } diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 270cfa06e5815..9371fc14dd8a0 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use base_db::{salsa, CrateId, SourceDatabase, Upcast}; use either::Either; -use hir_expand::{db::AstDatabase, HirFileId}; +use hir_expand::{db::ExpandDatabase, HirFileId}; use intern::Interned; use la_arena::ArenaMap; use syntax::{ast, AstPtr}; @@ -64,7 +64,7 @@ pub trait InternDatabase: SourceDatabase { } #[salsa::query_group(DefDatabaseStorage)] -pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { +pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast { #[salsa::input] fn enable_proc_attr_macros(&self) -> bool; diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index bbea608c55eb5..19fa6b25419e1 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -245,6 +245,7 @@ pub enum Expr { pub enum ClosureKind { Closure, Generator(Movability), + Async, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/hir-def/src/macro_expansion_tests.rs b/crates/hir-def/src/macro_expansion_tests.rs index 5ab90d92d9bd1..314bf22b95ee7 100644 --- a/crates/hir-def/src/macro_expansion_tests.rs +++ b/crates/hir-def/src/macro_expansion_tests.rs @@ -20,7 +20,7 @@ use ::mbe::TokenMap; use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase}; use expect_test::Expect; use hir_expand::{ - db::{AstDatabase, TokenExpander}, + db::{ExpandDatabase, TokenExpander}, AstId, InFile, MacroDefId, MacroDefKind, MacroFile, }; use stdx::format_to; diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 7d7240e7e8cb3..4efe8c58a69e3 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -120,6 +120,8 @@ pub struct DefMap { registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. unstable_features: FxHashSet, + /// #[rustc_coherence_is_core] + rustc_coherence_is_core: bool, edition: Edition, recursion_limit: Option, @@ -215,7 +217,7 @@ pub struct ModuleData { pub origin: ModuleOrigin, /// Declared visibility of this module. pub visibility: Visibility, - + /// Always [`None`] for block modules pub parent: Option, pub children: FxHashMap, pub scope: ItemScope, @@ -292,6 +294,7 @@ impl DefMap { registered_tools: Vec::new(), unstable_features: FxHashSet::default(), diagnostics: Vec::new(), + rustc_coherence_is_core: false, } } @@ -325,6 +328,10 @@ impl DefMap { self.unstable_features.contains(feature) } + pub fn is_rustc_coherence_is_core(&self) -> bool { + self.rustc_coherence_is_core + } + pub fn root(&self) -> LocalModuleId { self.root } @@ -337,7 +344,7 @@ impl DefMap { self.proc_macro_loading_error.as_deref() } - pub(crate) fn krate(&self) -> CrateId { + pub fn krate(&self) -> CrateId { self.krate } @@ -425,7 +432,7 @@ impl DefMap { Some(self.block?.parent) } - /// Returns the module containing `local_mod`, either the parent `mod`, or the module containing + /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(&self, local_mod: LocalModuleId) -> Option { match self[local_mod].parent { @@ -498,6 +505,7 @@ impl DefMap { krate: _, prelude: _, root: _, + rustc_coherence_is_core: _, } = self; extern_prelude.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 70acc3442c306..ddcee77ec4ccf 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -87,10 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T // FIXME: a hacky way to create a Name from string. let name = tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; - ( - name.as_name(), - ProcMacroExpander::new(def_map.krate, base_db::ProcMacroId(idx as u32)), - ) + (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) }) .collect() } @@ -299,6 +296,11 @@ impl DefCollector<'_> { continue; } + if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { + self.def_map.rustc_coherence_is_core = true; + continue; + } + if *attr_name == hir_expand::name![feature] { let features = attr.parse_path_comma_token_tree().into_iter().flatten().filter_map( @@ -581,7 +583,7 @@ impl DefCollector<'_> { let kind = def.kind.to_basedb_kind(); let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { Some(&(_, expander)) => (expander, kind), - None => (ProcMacroExpander::dummy(self.def_map.krate), kind), + None => (ProcMacroExpander::dummy(), kind), }; let proc_macro_id = diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index b7908bddaa1cf..ee143b19ae5b3 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -9,7 +9,7 @@ use base_db::{ salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase, Upcast, }; -use hir_expand::{db::AstDatabase, InFile}; +use hir_expand::{db::ExpandDatabase, InFile}; use stdx::hash::NoHashHashSet; use syntax::{algo, ast, AstNode}; @@ -23,7 +23,7 @@ use crate::{ #[salsa::database( base_db::SourceDatabaseExtStorage, base_db::SourceDatabaseStorage, - hir_expand::db::AstDatabaseStorage, + hir_expand::db::ExpandDatabaseStorage, crate::db::InternDatabaseStorage, crate::db::DefDatabaseStorage )] @@ -40,8 +40,8 @@ impl Default for TestDB { } } -impl Upcast for TestDB { - fn upcast(&self) -> &(dyn AstDatabase + 'static) { +impl Upcast for TestDB { + fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { &*self } } diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index c9fcaae56cf0c..ab76ed43d3a0e 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -131,21 +131,23 @@ impl Visibility { // visibility as the containing module (even though no items are directly nameable from // there, getting this right is important for method resolution). // In that case, we adjust the visibility of `to_module` to point to the containing module. + // Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're // currently computing, so we must not call the `def_map` query for it. - let arc; - let to_module_def_map = - if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() { - cov_mark::hit!(is_visible_from_same_block_def_map); - def_map - } else { - arc = to_module.def_map(db); - &arc - }; - let is_block_root = - to_module.block.is_some() && to_module_def_map[to_module.local_id].parent.is_none(); - if is_block_root { - to_module = to_module_def_map.containing_module(to_module.local_id).unwrap(); + let mut arc; + loop { + let to_module_def_map = + if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() { + cov_mark::hit!(is_visible_from_same_block_def_map); + def_map + } else { + arc = to_module.def_map(db); + &arc + }; + match to_module_def_map.parent() { + Some(parent) => to_module = parent, + None => break, + } } // from_module needs to be a descendant of to_module diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index 5c04f8e8b8f37..8d1e88725ecbf 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -10,7 +10,7 @@ use smallvec::{smallvec, SmallVec}; use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode}; use crate::{ - db::AstDatabase, + db::ExpandDatabase, hygiene::Hygiene, mod_path::{ModPath, PathKind}, name::AsName, @@ -38,7 +38,7 @@ impl ops::Deref for RawAttrs { impl RawAttrs { pub const EMPTY: Self = Self { entries: None }; - pub fn new(db: &dyn AstDatabase, owner: &dyn ast::HasAttrs, hygiene: &Hygiene) -> Self { + pub fn new(db: &dyn ExpandDatabase, owner: &dyn ast::HasAttrs, hygiene: &Hygiene) -> Self { let entries = collect_attrs(owner) .filter_map(|(id, attr)| match attr { Either::Left(attr) => { @@ -55,7 +55,7 @@ impl RawAttrs { Self { entries: if entries.is_empty() { None } else { Some(entries) } } } - pub fn from_attrs_owner(db: &dyn AstDatabase, owner: InFile<&dyn ast::HasAttrs>) -> Self { + pub fn from_attrs_owner(db: &dyn ExpandDatabase, owner: InFile<&dyn ast::HasAttrs>) -> Self { let hygiene = Hygiene::new(db, owner.file_id); Self::new(db, owner.value, &hygiene) } @@ -87,7 +87,7 @@ impl RawAttrs { /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. // FIXME: This should return a different type - pub fn filter(self, db: &dyn AstDatabase, krate: CrateId) -> RawAttrs { + pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs { let has_cfg_attrs = self .iter() .any(|attr| attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr])); @@ -199,7 +199,7 @@ impl fmt::Display for AttrInput { impl Attr { fn from_src( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, ast: ast::Meta, hygiene: &Hygiene, id: AttrId, @@ -221,7 +221,7 @@ impl Attr { } fn from_tt( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, tt: &tt::Subtree, hygiene: &Hygiene, id: AttrId, diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs index 906ca991d73be..277ecd9399422 100644 --- a/crates/hir-expand/src/builtin_attr_macro.rs +++ b/crates/hir-expand/src/builtin_attr_macro.rs @@ -1,6 +1,6 @@ //! Builtin attributes. -use crate::{db::AstDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind}; +use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind}; macro_rules! register_builtin { ( $(($name:ident, $variant:ident) => $expand:ident),* ) => { @@ -12,7 +12,7 @@ macro_rules! register_builtin { impl BuiltinAttrExpander { pub fn expand( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -60,7 +60,7 @@ pub fn find_builtin_attr(ident: &name::Name) -> Option { } fn dummy_attr_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -90,7 +90,7 @@ fn dummy_attr_expand( /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in /// [`hir::Semantics`] to make this work. fn derive_attr_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 060a680542fd9..5c1a75132ee94 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -9,7 +9,7 @@ use syntax::{ match_ast, }; -use crate::{db::AstDatabase, name, quote, ExpandError, ExpandResult, MacroCallId}; +use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -21,7 +21,7 @@ macro_rules! register_builtin { impl BuiltinDeriveExpander { pub fn expand( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -141,7 +141,7 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu ExpandResult::ok(expanded) } -fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree { +fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree { // FIXME: make hygiene works for builtin derive macro // such that $crate can be used here. let cg = db.crate_graph(); @@ -158,7 +158,7 @@ fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree { } fn copy_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -167,7 +167,7 @@ fn copy_expand( } fn clone_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -176,7 +176,7 @@ fn clone_expand( } fn default_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -185,7 +185,7 @@ fn default_expand( } fn debug_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -194,7 +194,7 @@ fn debug_expand( } fn hash_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -202,13 +202,17 @@ fn hash_expand( expand_simple_derive(tt, quote! { #krate::hash::Hash }) } -fn eq_expand(db: &dyn AstDatabase, id: MacroCallId, tt: &tt::Subtree) -> ExpandResult { +fn eq_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { let krate = find_builtin_crate(db, id); expand_simple_derive(tt, quote! { #krate::cmp::Eq }) } fn partial_eq_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -217,7 +221,7 @@ fn partial_eq_expand( } fn ord_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -226,7 +230,7 @@ fn ord_expand( } fn partial_ord_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 295083a37f232..44510f2b7ff68 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -10,7 +10,7 @@ use syntax::{ }; use crate::{ - db::AstDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc, + db::ExpandDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc, }; macro_rules! register_builtin { @@ -28,7 +28,7 @@ macro_rules! register_builtin { impl BuiltinFnLikeExpander { pub fn expand( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -42,7 +42,7 @@ macro_rules! register_builtin { impl EagerExpander { pub fn expand( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -121,7 +121,7 @@ const DOLLAR_CRATE: tt::Ident = tt::Ident { text: SmolStr::new_inline("$crate"), span: tt::TokenId::unspecified() }; fn module_path_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -130,7 +130,7 @@ fn module_path_expand( } fn line_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -144,7 +144,7 @@ fn line_expand( } fn log_syntax_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -152,7 +152,7 @@ fn log_syntax_expand( } fn trace_macros_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -160,7 +160,7 @@ fn trace_macros_expand( } fn stringify_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -174,7 +174,7 @@ fn stringify_expand( } fn column_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -188,7 +188,7 @@ fn column_expand( } fn assert_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -218,7 +218,7 @@ fn assert_expand( } fn file_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -234,7 +234,7 @@ fn file_expand( } fn format_args_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -276,7 +276,7 @@ fn format_args_expand( } fn asm_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -304,7 +304,7 @@ fn asm_expand( } fn global_asm_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, _tt: &tt::Subtree, ) -> ExpandResult { @@ -313,7 +313,7 @@ fn global_asm_expand( } fn cfg_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -325,7 +325,7 @@ fn cfg_expand( } fn panic_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -343,7 +343,7 @@ fn panic_expand( } fn unreachable_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -379,7 +379,7 @@ fn unquote_byte_string(lit: &tt::Literal) -> Option> { } fn compile_error_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -395,7 +395,7 @@ fn compile_error_expand( } fn concat_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -441,7 +441,7 @@ fn concat_expand( } fn concat_bytes_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -507,7 +507,7 @@ fn concat_bytes_expand_subtree( } fn concat_idents_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -529,7 +529,7 @@ fn concat_idents_expand( } fn relative_file( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, call_id: MacroCallId, path_str: &str, allow_recursion: bool, @@ -558,7 +558,7 @@ fn parse_string(tt: &tt::Subtree) -> Result { } fn include_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -583,7 +583,7 @@ fn include_expand( } fn include_bytes_expand( - _db: &dyn AstDatabase, + _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -606,7 +606,7 @@ fn include_bytes_expand( } fn include_str_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -637,13 +637,13 @@ fn include_str_expand( ExpandResult::ok(ExpandedEager::new(quote!(#text))) } -fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option { +fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; db.crate_graph()[krate].env.get(key) } fn env_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -679,7 +679,7 @@ fn env_expand( } fn option_env_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 76016274f0e85..45572499e8426 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -44,7 +44,7 @@ pub enum TokenExpander { impl TokenExpander { fn expand( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { @@ -83,9 +83,8 @@ impl TokenExpander { } } -// FIXME: rename to ExpandDatabase -#[salsa::query_group(AstDatabaseStorage)] -pub trait AstDatabase: SourceDatabase { +#[salsa::query_group(ExpandDatabaseStorage)] +pub trait ExpandDatabase: SourceDatabase { fn ast_id_map(&self, file_id: HirFileId) -> Arc; /// Main public API -- parses a hir file, not caring whether it's a real @@ -138,7 +137,7 @@ pub trait AstDatabase: SourceDatabase { /// token. The `token_to_map` mapped down into the expansion, with the mapped /// token returned. pub fn expand_speculative( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, actual_macro_call: MacroCallId, speculative_args: &SyntaxNode, token_to_map: SyntaxToken, @@ -211,7 +210,7 @@ pub fn expand_speculative( let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(expander, ..) => { tt.delimiter = tt::Delimiter::unspecified(); - expander.expand(db, loc.krate, &tt, attr_arg.as_ref()) + expander.expand(db, loc.def.krate, loc.krate, &tt, attr_arg.as_ref()) } MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?) @@ -236,12 +235,12 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } -fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { +fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc { let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default(); Arc::new(map) } -fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option { +fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option { match file_id.repr() { HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), HirFileIdRepr::MacroFile(macro_file) => { @@ -253,13 +252,13 @@ fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option ExpandResult, Arc)>> { let _p = profile::span("parse_macro_expansion"); - let result = db.macro_expand(macro_file.macro_call_id); + let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id); - if let Some(err) = &result.err { + if let Some(err) = &err { // Note: // The final goal we would like to make all parse_macro success, // such that the following log will not call anyway. @@ -280,9 +279,9 @@ fn parse_macro_expansion( parents ); } - let tt = match result.value { + let tt = match value { Some(tt) => tt, - None => return ExpandResult { value: None, err: result.err }, + None => return ExpandResult { value: None, err }, }; let expand_to = macro_expand_to(db, macro_file.macro_call_id); @@ -292,11 +291,11 @@ fn parse_macro_expansion( let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to); - ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: result.err } + ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err } } fn macro_arg( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, id: MacroCallId, ) -> Option> { let arg = db.macro_arg_text(id)?; @@ -357,7 +356,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet Option { +fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option { let loc = db.lookup_intern_macro_call(id); let arg = loc.kind.arg(db)?; if matches!(loc.kind, MacroCallKind::FnLike { .. }) { @@ -380,7 +379,10 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option { Some(arg.green().into()) } -fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Result, mbe::ParseError> { +fn macro_def( + db: &dyn ExpandDatabase, + id: MacroDefId, +) -> Result, mbe::ParseError> { match id.kind { MacroDefKind::Declarative(ast_id) => { let (mac, def_site_token_map) = match ast_id.to_node(db) { @@ -419,7 +421,10 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Result, } } -fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult>> { +fn macro_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, +) -> ExpandResult>> { let _p = profile::span("macro_expand"); let loc: MacroCallLoc = db.lookup_intern_macro_call(id); if let Some(eager) = &loc.eager { @@ -469,11 +474,11 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult Option { +fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option { db.macro_expand(macro_call).err } -fn expand_proc_macro(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult { +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { let loc: MacroCallLoc = db.lookup_intern_macro_call(id); let macro_arg = match db.macro_arg(id) { Some(it) => it, @@ -499,14 +504,14 @@ fn expand_proc_macro(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult None, }; - expander.expand(db, loc.krate, ¯o_arg.0, attr_arg.as_ref()) + expander.expand(db, loc.def.krate, loc.krate, ¯o_arg.0, attr_arg.as_ref()) } -fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { +fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc { Arc::new(HygieneFrame::new(db, file_id)) } -fn macro_expand_to(db: &dyn AstDatabase, id: MacroCallId) -> ExpandTo { +fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo { let loc: MacroCallLoc = db.lookup_intern_macro_call(id); loc.kind.expand_to() } diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index dfab7ec92c763..aca41b11f926e 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -25,7 +25,7 @@ use syntax::{ted, SyntaxNode}; use crate::{ ast::{self, AstNode}, - db::AstDatabase, + db::ExpandDatabase, hygiene::Hygiene, mod_path::ModPath, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind, @@ -96,7 +96,7 @@ impl ErrorSink for &'_ mut dyn FnMut(ExpandError) { } pub fn expand_eager_macro( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, krate: CrateId, macro_call: InFile, def: MacroDefId, @@ -172,7 +172,7 @@ fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree { } fn lazy_expand( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, def: &MacroDefId, macro_call: InFile, krate: CrateId, @@ -193,7 +193,7 @@ fn lazy_expand( } fn eager_macro_recur( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, hygiene: &Hygiene, curr: InFile, krate: CrateId, diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index c811d1c66a82d..b273f21768c68 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -636,9 +636,8 @@ fn foo() { if {} } "#, - // the {} gets parsed as the condition, I think? expect![[r#" -fn foo () {if {} {}} +fn foo () {if __ra_fixup {} {}} "#]], ) } diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index 2300ee9d08998..2eb56fc9e8b26 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -14,7 +14,7 @@ use syntax::{ }; use crate::{ - db::{self, AstDatabase}, + db::{self, ExpandDatabase}, fixup, name::{AsName, Name}, HirFileId, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile, @@ -26,7 +26,7 @@ pub struct Hygiene { } impl Hygiene { - pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { + pub fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> Hygiene { Hygiene { frames: Some(HygieneFrames::new(db, file_id)) } } @@ -37,7 +37,7 @@ impl Hygiene { // FIXME: this should just return name pub fn name_ref_to_name( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, name_ref: ast::NameRef, ) -> Either { if let Some(frames) = &self.frames { @@ -51,7 +51,7 @@ impl Hygiene { Either::Left(name_ref.as_name()) } - pub fn local_inner_macros(&self, db: &dyn AstDatabase, path: ast::Path) -> Option { + pub fn local_inner_macros(&self, db: &dyn ExpandDatabase, path: ast::Path) -> Option { let mut token = path.syntax().first_token()?.text_range(); let frames = self.frames.as_ref()?; let mut current = &frames.0; @@ -87,13 +87,13 @@ pub struct HygieneFrame { } impl HygieneFrames { - fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { + fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> Self { // Note that this intentionally avoids the `hygiene_frame` query to avoid blowing up memory // usage. The query is only helpful for nested `HygieneFrame`s as it avoids redundant work. HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) } - fn root_crate(&self, db: &dyn AstDatabase, node: &SyntaxNode) -> Option { + fn root_crate(&self, db: &dyn ExpandDatabase, node: &SyntaxNode) -> Option { let mut token = node.first_token()?.text_range(); let mut result = self.0.krate; let mut current = self.0.clone(); @@ -136,7 +136,7 @@ struct HygieneInfo { impl HygieneInfo { fn map_ident_up( &self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, token: TextRange, ) -> Option<(InFile, Origin)> { let token_id = self.exp_map.token_by_range(token)?; @@ -175,7 +175,7 @@ impl HygieneInfo { } fn make_hygiene_info( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, macro_file: MacroFile, loc: &MacroCallLoc, ) -> Option { @@ -215,7 +215,7 @@ fn make_hygiene_info( } impl HygieneFrame { - pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame { + pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> HygieneFrame { let (info, krate, local_inner) = match file_id.macro_file() { None => (None, None, false), Some(macro_file) => { diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 39fc08ecdcfe3..5e99eacc1b619 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -198,7 +198,7 @@ impl HirFileId { /// For macro-expansion files, returns the file original source file the /// expansion originated from. - pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId { + pub fn original_file(self, db: &dyn db::ExpandDatabase) -> FileId { let mut file_id = self; loop { match file_id.repr() { @@ -214,7 +214,7 @@ impl HirFileId { } } - pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 { + pub fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32 { let mut level = 0; let mut curr = self; while let Some(macro_file) = curr.macro_file() { @@ -227,14 +227,14 @@ impl HirFileId { } /// If this is a macro call, returns the syntax node of the call. - pub fn call_node(self, db: &dyn db::AstDatabase) -> Option> { + pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option> { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); Some(loc.kind.to_node(db)) } /// If this is a macro call, returns the syntax node of the very first macro call this file resides in. - pub fn original_call_node(self, db: &dyn db::AstDatabase) -> Option<(FileId, SyntaxNode)> { + pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> { let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db); loop { @@ -248,7 +248,7 @@ impl HirFileId { } /// Return expansion information if it is a macro-expansion file - pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option { + pub fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); @@ -294,7 +294,7 @@ impl HirFileId { } /// Indicate it is macro file generated for builtin derive - pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option> { + pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> Option> { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let attr = match loc.def.kind { @@ -304,7 +304,7 @@ impl HirFileId { Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) } - pub fn is_custom_derive(&self, db: &dyn db::AstDatabase) -> bool { + pub fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); @@ -315,7 +315,7 @@ impl HirFileId { } /// Return whether this file is an include macro - pub fn is_include_macro(&self, db: &dyn db::AstDatabase) -> bool { + pub fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); @@ -326,7 +326,7 @@ impl HirFileId { } /// Return whether this file is an attr macro - pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool { + pub fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); @@ -338,7 +338,7 @@ impl HirFileId { /// Return whether this file is the pseudo expansion of the derive attribute. /// See [`crate::builtin_attr_macro::derive_attr_expand`]. - pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::AstDatabase) -> bool { + pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); @@ -384,7 +384,7 @@ impl HirFileId { impl MacroDefId { pub fn as_lazy_macro( self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, krate: CrateId, kind: MacroCallKind, ) -> MacroCallId { @@ -427,7 +427,7 @@ impl MacroCallKind { } } - pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile { + pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile { match self { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) @@ -465,7 +465,7 @@ impl MacroCallKind { /// Returns the original file range that best describes the location of this macro call. /// /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives. - pub fn original_call_range_with_body(self, db: &dyn db::AstDatabase) -> FileRange { + pub fn original_call_range_with_body(self, db: &dyn db::ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id().repr() { @@ -490,7 +490,7 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives /// get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange { + pub fn original_call_range(self, db: &dyn db::ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id().repr() { @@ -529,7 +529,7 @@ impl MacroCallKind { FileRange { range, file_id } } - fn arg(&self, db: &dyn db::AstDatabase) -> Option { + fn arg(&self, db: &dyn db::ExpandDatabase) -> Option { match self { MacroCallKind::FnLike { ast_id, .. } => { Some(ast_id.to_node(db).token_tree()?.syntax().clone()) @@ -597,7 +597,7 @@ impl ExpansionInfo { /// Both of these only have one simple call site input so no special handling is required here. pub fn map_token_down( &self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, item: Option, token: InFile<&SyntaxToken>, ) -> Option> + '_> { @@ -666,7 +666,7 @@ impl ExpansionInfo { /// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion. pub fn map_token_up( &self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, token: InFile<&SyntaxToken>, ) -> Option<(InFile, Origin)> { // Fetch the id through its text range, @@ -717,7 +717,7 @@ impl ExpansionInfo { pub type AstId = InFile>; impl AstId { - pub fn to_node(&self, db: &dyn db::AstDatabase) -> N { + pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N { let root = db.parse_or_expand(self.file_id).unwrap(); db.ast_id_map(self.file_id).get(self.value).to_node(&root) } @@ -753,7 +753,7 @@ impl InFile { self.with_value(&self.value) } - pub fn file_syntax(&self, db: &dyn db::AstDatabase) -> SyntaxNode { + pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { db.parse_or_expand(self.file_id).expect("source created from invalid file") } } @@ -783,7 +783,7 @@ impl InFile> { impl<'a> InFile<&'a SyntaxNode> { pub fn ancestors_with_macros( self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, ) -> impl Iterator> + Clone + '_ { iter::successors(Some(self.cloned()), move |node| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), @@ -794,7 +794,7 @@ impl<'a> InFile<&'a SyntaxNode> { /// Skips the attributed item that caused the macro invocation we are climbing up pub fn ancestors_with_macros_skip_attr_item( self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, ) -> impl Iterator> + '_ { let succ = move |node: &InFile| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), @@ -816,7 +816,7 @@ impl<'a> InFile<&'a SyntaxNode> { /// /// For attributes and derives, this will point back to the attribute only. /// For the entire item use [`InFile::original_file_range_full`]. - pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange { + pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::MacroFile(mac_file) => { @@ -831,7 +831,7 @@ impl<'a> InFile<&'a SyntaxNode> { } /// Falls back to the macro call range if the node cannot be mapped up fully. - pub fn original_file_range_full(self, db: &dyn db::AstDatabase) -> FileRange { + pub fn original_file_range_full(self, db: &dyn db::ExpandDatabase) -> FileRange { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::MacroFile(mac_file) => { @@ -846,7 +846,7 @@ impl<'a> InFile<&'a SyntaxNode> { } /// Attempts to map the syntax node back up its macro calls. - pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option { + pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option { match ascend_node_border_tokens(db, self) { Some(InFile { file_id, value: (first, last) }) => { let original_file = file_id.original_file(db); @@ -865,7 +865,7 @@ impl<'a> InFile<&'a SyntaxNode> { } } - pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option> { + pub fn original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { @@ -892,13 +892,13 @@ impl<'a> InFile<&'a SyntaxNode> { } impl InFile { - pub fn upmap(self, db: &dyn db::AstDatabase) -> Option> { + pub fn upmap(self, db: &dyn db::ExpandDatabase) -> Option> { let expansion = self.file_id.expansion_info(db)?; expansion.map_token_up(db, self.as_ref()).map(|(it, _)| it) } /// Falls back to the macro call range if the node cannot be mapped up fully. - pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange { + pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::MacroFile(mac_file) => { @@ -913,7 +913,7 @@ impl InFile { } /// Attempts to map the syntax node back up its macro calls. - pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option { + pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => { Some(FileRange { file_id, range: self.value.text_range() }) @@ -932,7 +932,7 @@ impl InFile { pub fn ancestors_with_macros( self, - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, ) -> impl Iterator> + '_ { self.value.parent().into_iter().flat_map({ let file_id = self.file_id; @@ -942,7 +942,7 @@ impl InFile { } fn ascend_node_border_tokens( - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, InFile { file_id, value: node }: InFile<&SyntaxNode>, ) -> Option> { let expansion = file_id.expansion_info(db)?; @@ -958,7 +958,7 @@ fn ascend_node_border_tokens( } fn ascend_call_token( - db: &dyn db::AstDatabase, + db: &dyn db::ExpandDatabase, expansion: &ExpansionInfo, token: InFile, ) -> Option> { @@ -977,7 +977,7 @@ impl InFile { self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) } - pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option> { + pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index d7586d129b768..e9393cc89aedf 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - db::AstDatabase, + db::ExpandDatabase, hygiene::Hygiene, name::{known, Name}, }; @@ -37,7 +37,11 @@ pub enum PathKind { } impl ModPath { - pub fn from_src(db: &dyn AstDatabase, path: ast::Path, hygiene: &Hygiene) -> Option { + pub fn from_src( + db: &dyn ExpandDatabase, + path: ast::Path, + hygiene: &Hygiene, + ) -> Option { convert_path(db, None, path, hygiene) } @@ -162,7 +166,7 @@ impl From for ModPath { } fn convert_path( - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, prefix: Option, path: ast::Path, hygiene: &Hygiene, diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index 3f4d2540c099a..d758e9302cd87 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -3,22 +3,20 @@ use base_db::{CrateId, ProcMacroExpansionError, ProcMacroId, ProcMacroKind}; use stdx::never; -use crate::{db::AstDatabase, tt, ExpandError, ExpandResult}; +use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct ProcMacroExpander { - krate: CrateId, proc_macro_id: Option, } impl ProcMacroExpander { - pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> Self { - Self { krate, proc_macro_id: Some(proc_macro_id) } + pub fn new(proc_macro_id: ProcMacroId) -> Self { + Self { proc_macro_id: Some(proc_macro_id) } } - pub fn dummy(krate: CrateId) -> Self { - // FIXME: Should store the name for better errors - Self { krate, proc_macro_id: None } + pub fn dummy() -> Self { + Self { proc_macro_id: None } } pub fn is_dummy(&self) -> bool { @@ -27,7 +25,8 @@ impl ProcMacroExpander { pub fn expand( self, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, + def_crate: CrateId, calling_crate: CrateId, tt: &tt::Subtree, attr_arg: Option<&tt::Subtree>, @@ -35,7 +34,7 @@ impl ProcMacroExpander { match self.proc_macro_id { Some(id) => { let krate_graph = db.crate_graph(); - let proc_macros = match &krate_graph[self.krate].proc_macro { + let proc_macros = match &krate_graph[def_crate].proc_macro { Ok(proc_macros) => proc_macros, Err(_) => { never!("Non-dummy expander even though there are no proc macros"); @@ -84,7 +83,7 @@ impl ProcMacroExpander { } None => ExpandResult::with_err( tt::Subtree::empty(), - ExpandError::UnresolvedProcMacro(self.krate), + ExpandError::UnresolvedProcMacro(def_crate), ), } } diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 4572e33486f0e..9b3296df2508a 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -22,10 +22,10 @@ either = "1.7.0" tracing = "0.1.35" rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.88.0", default-features = false } -chalk-ir = "0.88.0" -chalk-recursive = { version = "0.88.0", default-features = false } -chalk-derive = "0.88.0" +chalk-solve = { version = "0.89.0", default-features = false } +chalk-ir = "0.89.0" +chalk-recursive = { version = "0.89.0", default-features = false } +chalk-derive = "0.89.0" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } once_cell = "1.17.0" typed-arena = "2.0.1" diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index e6aefbf27167a..2141894922f7b 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -12,7 +12,7 @@ use hir_def::{ use crate::{ db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders, - CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, + CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, }; @@ -378,6 +378,19 @@ impl ProjectionTyExt for ProjectionTy { } } +pub trait DynTyExt { + fn principal(&self) -> Option<&TraitRef>; +} + +impl DynTyExt for DynTy { + fn principal(&self) -> Option<&TraitRef> { + self.bounds.skip_binders().interned().get(0).and_then(|b| match b.skip_binders() { + crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), + _ => None, + }) + } +} + pub trait TraitRefExt { fn hir_trait_id(&self) -> TraitId; } diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs index 37eb06be1d3d1..4b147b99707c1 100644 --- a/crates/hir-ty/src/diagnostics.rs +++ b/crates/hir-ty/src/diagnostics.rs @@ -11,3 +11,9 @@ pub use crate::diagnostics::{ }, unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr}, }; + +#[derive(Debug, PartialEq, Eq)] +pub struct IncoherentImpl { + pub file_id: hir_expand::HirFileId, + pub impl_: syntax::AstPtr, +} diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 535189ff0288b..ee186673ee130 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -275,7 +275,23 @@ impl<'a> InferenceContext<'a> { Some(type_ref) => self.make_ty(type_ref), None => self.table.new_type_var(), }; - sig_tys.push(ret_ty.clone()); + if let ClosureKind::Async = closure_kind { + // Use the first type parameter as the output type of future. + // existential type AsyncBlockImplTrait: Future + let impl_trait_id = + crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); + let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); + sig_tys.push( + TyKind::OpaqueType( + opaque_ty_id, + Substitution::from1(Interner, ret_ty.clone()), + ) + .intern(Interner), + ); + } else { + sig_tys.push(ret_ty.clone()); + } + let sig_ty = TyKind::Function(FnPointer { num_binders: 0, sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false }, @@ -286,33 +302,38 @@ impl<'a> InferenceContext<'a> { }) .intern(Interner); - let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) { - // FIXME: report error when there are more than 1 parameter. - let resume_ty = match sig_tys.first() { - // When `sig_tys.len() == 1` the first type is the return type, not the - // first parameter type. - Some(ty) if sig_tys.len() > 1 => ty.clone(), - _ => self.result.standard_types.unit.clone(), - }; - let yield_ty = self.table.new_type_var(); - - let subst = TyBuilder::subst_for_generator(self.db, self.owner) - .push(resume_ty.clone()) - .push(yield_ty.clone()) - .push(ret_ty.clone()) - .build(); + let (ty, resume_yield_tys) = match closure_kind { + ClosureKind::Generator(_) => { + // FIXME: report error when there are more than 1 parameter. + let resume_ty = match sig_tys.first() { + // When `sig_tys.len() == 1` the first type is the return type, not the + // first parameter type. + Some(ty) if sig_tys.len() > 1 => ty.clone(), + _ => self.result.standard_types.unit.clone(), + }; + let yield_ty = self.table.new_type_var(); + + let subst = TyBuilder::subst_for_generator(self.db, self.owner) + .push(resume_ty.clone()) + .push(yield_ty.clone()) + .push(ret_ty.clone()) + .build(); - let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into(); - let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner); + let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into(); + let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner); - (generator_ty, Some((resume_ty, yield_ty))) - } else { - let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); - let closure_ty = - TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone())) - .intern(Interner); + (generator_ty, Some((resume_ty, yield_ty))) + } + ClosureKind::Closure | ClosureKind::Async => { + let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); + let closure_ty = TyKind::Closure( + closure_id, + Substitution::from1(Interner, sig_ty.clone()), + ) + .intern(Interner); - (closure_ty, None) + (closure_ty, None) + } }; // Eagerly try to relate the closure type with the expected @@ -321,7 +342,7 @@ impl<'a> InferenceContext<'a> { self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected); // Now go through the argument patterns - for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { + for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) { self.infer_top_pat(*arg_pat, &arg_ty); } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 0f49e83788183..5f839fc307aab 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -5,10 +5,7 @@ use std::iter::repeat_with; use chalk_ir::Mutability; use hir_def::{ body::Body, - expr::{ - Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId, - RecordFieldPat, - }, + expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, path::Path, }; use hir_expand::name::Name; @@ -439,38 +436,8 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { let mut res = false; - walk_pats(body, pat_id, &mut |pat| { + body.walk_pats(pat_id, &mut |pat| { res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref); }); res } - -fn walk_pats(body: &Body, pat_id: PatId, f: &mut impl FnMut(&Pat)) { - let pat = &body[pat_id]; - f(pat); - match pat { - Pat::Range { .. } - | Pat::Lit(..) - | Pat::Path(..) - | Pat::ConstBlock(..) - | Pat::Wild - | Pat::Missing => {} - &Pat::Bind { subpat, .. } => { - if let Some(subpat) = subpat { - walk_pats(body, subpat, f); - } - } - Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { - args.iter().copied().for_each(|p| walk_pats(body, p, f)); - } - Pat::Ref { pat, .. } => walk_pats(body, *pat, f), - Pat::Slice { prefix, slice, suffix } => { - let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); - total_iter.copied().for_each(|p| walk_pats(body, p, f)); - } - Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| walk_pats(body, *pat, f)); - } - Pat::Box { inner } => walk_pats(body, *inner, f), - } -} diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 92a17fc3a9972..f3a27632bf545 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -19,13 +19,13 @@ use stdx::never; use crate::{ autoderef::{self, AutoderefKind}, db::HirDatabase, - from_foreign_def_id, + from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner, - Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment, + Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, }; /// This is used as a key for indexing impls. @@ -266,11 +266,12 @@ impl TraitImpls { #[derive(Debug, Eq, PartialEq)] pub struct InherentImpls { map: FxHashMap>, + invalid_impls: Vec, } impl InherentImpls { pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; let crate_def_map = db.crate_def_map(krate); impls.collect_def_map(db, &crate_def_map); @@ -283,7 +284,7 @@ impl InherentImpls { db: &dyn HirDatabase, block: BlockId, ) -> Option> { - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; if let Some(block_def_map) = db.block_def_map(block) { impls.collect_def_map(db, &block_def_map); impls.shrink_to_fit(); @@ -306,11 +307,17 @@ impl InherentImpls { } let self_ty = db.impl_self_ty(impl_id); - let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); - if let Some(fp) = fp { - self.map.entry(fp).or_default().push(impl_id); + let self_ty = self_ty.skip_binders(); + + match is_inherent_impl_coherent(db, def_map, &data, self_ty) { + true => { + // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) + if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) { + self.map.entry(fp).or_default().push(impl_id); + } + } + false => self.invalid_impls.push(impl_id), } - // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) } // To better support custom derives, collect impls in all unnamed const items. @@ -334,6 +341,10 @@ impl InherentImpls { pub fn all_impls(&self) -> impl Iterator + '_ { self.map.values().flat_map(|v| v.iter().copied()) } + + pub fn invalid_impls(&self) -> &[ImplId] { + &self.invalid_impls + } } pub(crate) fn incoherent_inherent_impl_crates( @@ -775,6 +786,69 @@ fn find_matching_impl( } } +fn is_inherent_impl_coherent( + db: &dyn HirDatabase, + def_map: &DefMap, + impl_data: &ImplData, + self_ty: &Ty, +) -> bool { + let self_ty = self_ty.kind(Interner); + let impl_allowed = match self_ty { + TyKind::Tuple(_, _) + | TyKind::FnDef(_, _) + | TyKind::Array(_, _) + | TyKind::Never + | TyKind::Raw(_, _) + | TyKind::Ref(_, _, _) + | TyKind::Slice(_) + | TyKind::Str + | TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(), + + &TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(), + TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { + from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate() == def_map.krate() + }), + + _ => true, + }; + impl_allowed || { + let rustc_has_incoherent_inherent_impls = match self_ty { + TyKind::Tuple(_, _) + | TyKind::FnDef(_, _) + | TyKind::Array(_, _) + | TyKind::Never + | TyKind::Raw(_, _) + | TyKind::Ref(_, _, _) + | TyKind::Slice(_) + | TyKind::Str + | TyKind::Scalar(_) => true, + + &TyKind::Adt(AdtId(adt), _) => match adt { + hir_def::AdtId::StructId(it) => { + db.struct_data(it).rustc_has_incoherent_inherent_impls + } + hir_def::AdtId::UnionId(it) => { + db.union_data(it).rustc_has_incoherent_inherent_impls + } + hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls, + }, + TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { + db.trait_data(from_chalk_trait_id(trait_ref.trait_id)) + .rustc_has_incoherent_inherent_impls + }), + + _ => false, + }; + rustc_has_incoherent_inherent_impls + && !impl_data.items.is_empty() + && impl_data.items.iter().copied().all(|assoc| match assoc { + AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl, + AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl, + AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl, + }) + } +} + pub fn iterate_path_candidates( ty: &Canonical, db: &dyn HirDatabase, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 435a914088bcf..c4dd7c0ace46c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1113,7 +1113,7 @@ impl MirLowerCtx<'_> { if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { binding_mode = mode; } - self.push_storage_live(*id, current)?; + self.push_storage_live(*id, current); self.push_assignment( current, target_place.into(), @@ -1327,8 +1327,9 @@ impl MirLowerCtx<'_> { 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<()> { + /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in + /// the appropriated places. + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) { // 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 @@ -1356,7 +1357,6 @@ impl MirLowerCtx<'_> { 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 { @@ -1381,10 +1381,10 @@ impl MirLowerCtx<'_> { 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); - }; + self.lower_expr_as_place(current, *expr_id, true)? + else { + return Ok(None); + }; current = c; (current, else_block) = self.pattern_match( current, @@ -1407,6 +1407,10 @@ impl MirLowerCtx<'_> { } } } + } else { + self.body.walk_bindings_in_pat(*pat, |b| { + self.push_storage_live(b, current); + }); } } hir_def::expr::Statement::Expr { expr, has_semi: _ } => { diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs index 118e5311e9a64..8c48331b94b53 100644 --- a/crates/hir-ty/src/test_db.rs +++ b/crates/hir-ty/src/test_db.rs @@ -9,7 +9,7 @@ use base_db::{ salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; use hir_def::{db::DefDatabase, ModuleId}; -use hir_expand::db::AstDatabase; +use hir_expand::db::ExpandDatabase; use stdx::hash::{NoHashHashMap, NoHashHashSet}; use syntax::TextRange; use test_utils::extract_annotations; @@ -17,7 +17,7 @@ use test_utils::extract_annotations; #[salsa::database( base_db::SourceDatabaseExtStorage, base_db::SourceDatabaseStorage, - hir_expand::db::AstDatabaseStorage, + hir_expand::db::ExpandDatabaseStorage, hir_def::db::InternDatabaseStorage, hir_def::db::DefDatabaseStorage, crate::db::HirDatabaseStorage @@ -41,8 +41,8 @@ impl fmt::Debug for TestDB { } } -impl Upcast for TestDB { - fn upcast(&self) -> &(dyn AstDatabase + 'static) { +impl Upcast for TestDB { + fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { &*self } } diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index bcd63d9472a81..83d31f002a1dc 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -23,7 +23,7 @@ use hir_def::{ src::HasSource, AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, }; -use hir_expand::{db::AstDatabase, InFile}; +use hir_expand::{db::ExpandDatabase, InFile}; use once_cell::race::OnceBool; use stdx::format_to; use syntax::{ diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index e568e7013fac0..378d478336102 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -9,6 +9,7 @@ fn infer_slice_method() { check_types( r#" impl [T] { + #[rustc_allow_incoherent_impl] fn foo(&self) -> T { loop {} } @@ -35,6 +36,7 @@ fn test() { //- /lib.rs crate:other_crate mod foo { impl f32 { + #[rustc_allow_incoherent_impl] pub fn foo(self) -> f32 { 0. } } } @@ -47,6 +49,7 @@ fn infer_array_inherent_impl() { check_types( r#" impl [T; N] { + #[rustc_allow_incoherent_impl] fn foo(&self) -> T { loop {} } @@ -1437,6 +1440,7 @@ fn resolve_const_generic_array_methods() { r#" #[lang = "array"] impl [T; N] { + #[rustc_allow_incoherent_impl] pub fn map(self, f: F) -> [U; N] where F: FnMut(T) -> U, @@ -1445,6 +1449,7 @@ impl [T; N] { #[lang = "slice"] impl [T] { + #[rustc_allow_incoherent_impl] pub fn map(self, f: F) -> &[U] where F: FnMut(T) -> U, @@ -1468,6 +1473,7 @@ struct Const; #[lang = "array"] impl [T; N] { + #[rustc_allow_incoherent_impl] pub fn my_map(self, f: F, c: Const) -> [U; X] where F: FnMut(T) -> U, @@ -1476,6 +1482,7 @@ impl [T; N] { #[lang = "slice"] impl [T] { + #[rustc_allow_incoherent_impl] pub fn my_map(self, f: F, c: Const) -> &[U] where F: FnMut(T) -> U, @@ -1874,14 +1881,14 @@ fn incoherent_impls() { pub struct Box(T); use core::error::Error; -#[rustc_allow_incoherent_impl] impl dyn Error { + #[rustc_allow_incoherent_impl] pub fn downcast(self: Box) -> Result, Box> { loop {} } } -#[rustc_allow_incoherent_impl] impl dyn Error + Send { + #[rustc_allow_incoherent_impl] /// Attempts to downcast the box to a concrete type. pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index e6b4f13c8d113..689f0da44f680 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1756,3 +1756,35 @@ const C: usize = 2 + 2; "#, ); } + +#[test] +fn regression_14164() { + check_types( + r#" +trait Rec { + type K; + type Rebind: Rec; +} + +trait Expr { + type Part: Rec; + fn foo(_: ::Rebind) {} +} + +struct Head(K); +impl Rec for Head { + type K = K; + type Rebind = Head; +} + +fn test() +where + E: Expr>, +{ + let head; + //^^^^ Head + E::foo(head); +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 0e9c349afef36..13cc3fea52d16 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -1116,21 +1116,22 @@ fn infer_inherent_method() { fn infer_inherent_method_str() { check_infer( r#" - #[lang = "str"] - impl str { - fn foo(&self) -> i32 {} - } +#![rustc_coherence_is_core] +#[lang = "str"] +impl str { + fn foo(&self) -> i32 {} +} - fn test() { - "foo".foo(); - } - "#, +fn test() { + "foo".foo(); +} +"#, expect![[r#" - 39..43 'self': &str - 52..54 '{}': i32 - 68..88 '{ ...o(); }': () - 74..79 '"foo"': &str - 74..85 '"foo".foo()': i32 + 67..71 'self': &str + 80..82 '{}': i32 + 96..116 '{ ...o(); }': () + 102..107 '"foo"': &str + 102..113 '"foo".foo()': i32 "#]], ); } @@ -2640,6 +2641,7 @@ impl [T] {} #[lang = "slice_alloc"] impl [T] { + #[rustc_allow_incoherent_impl] pub fn into_vec(self: Box) -> Vec { unimplemented!() } @@ -2655,22 +2657,22 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 569..573 'self': Box<[T], A> - 602..634 '{ ... }': Vec - 648..761 '{ ...t]); }': () - 658..661 'vec': Vec - 664..679 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec - 664..691 '<[_]>:...1i32])': Vec - 680..690 'box [1i32]': Box<[i32; 1], Global> - 684..690 '[1i32]': [i32; 1] - 685..689 '1i32': i32 - 701..702 'v': Vec, Global> - 722..739 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> - 722..758 '<[_]> ...ruct])': Vec, Global> - 740..757 'box [b...truct]': Box<[Box; 1], Global> - 744..757 '[box Astruct]': [Box; 1] - 745..756 'box Astruct': Box - 749..756 'Astruct': Astruct + 604..608 'self': Box<[T], A> + 637..669 '{ ... }': Vec + 683..796 '{ ...t]); }': () + 693..696 'vec': Vec + 699..714 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec + 699..726 '<[_]>:...1i32])': Vec + 715..725 'box [1i32]': Box<[i32; 1], Global> + 719..725 '[1i32]': [i32; 1] + 720..724 '1i32': i32 + 736..737 'v': Vec, Global> + 757..774 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> + 757..793 '<[_]> ...ruct])': Vec, Global> + 775..792 'box [b...truct]': Box<[Box; 1], Global> + 779..792 '[box Astruct]': [Box; 1] + 780..791 'box Astruct': Box + 784..791 'Astruct': Astruct "#]], ) } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 015085bde4563..da76d7fd83f76 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -82,6 +82,46 @@ async fn test() { ); } +#[test] +fn infer_async_closure() { + check_types( + r#" +//- minicore: future, option +async fn test() { + let f = async move |x: i32| x + 42; + f; +// ^ |i32| -> impl Future + let a = f(4); + a; +// ^ impl Future + let x = a.await; + x; +// ^ i32 + let f = async move || 42; + f; +// ^ || -> impl Future + let a = f(); + a; +// ^ impl Future + let x = a.await; + x; +// ^ i32 + let b = ((async move || {})()).await; + b; +// ^ () + let c = async move || { + let y = None; + y + // ^ Option + }; + let _: Option = c().await; + c; +// ^ || -> impl Future> +} +"#, + ); +} + #[test] fn auto_sized_async_block() { check_no_mismatches( @@ -493,29 +533,30 @@ fn tuple_struct_with_fn() { r#" struct S(fn(u32) -> u64); fn test() -> u64 { - let a = S(|i| 2*i); + let a = S(|i| 2*i as u64); let b = a.0(4); a.0(2) }"#, expect![[r#" - 43..101 '{ ...0(2) }': u64 + 43..108 '{ ...0(2) }': u64 53..54 'a': S 57..58 'S': S(fn(u32) -> u64) -> S - 57..67 'S(|i| 2*i)': S - 59..66 '|i| 2*i': |u32| -> u64 + 57..74 'S(|i| ...s u64)': S + 59..73 '|i| 2*i as u64': |u32| -> u64 60..61 'i': u32 - 63..64 '2': u32 - 63..66 '2*i': u32 + 63..64 '2': u64 + 63..73 '2*i as u64': u64 65..66 'i': u32 - 77..78 'b': u64 - 81..82 'a': S - 81..84 'a.0': fn(u32) -> u64 - 81..87 'a.0(4)': u64 - 85..86 '4': u32 - 93..94 'a': S - 93..96 'a.0': fn(u32) -> u64 - 93..99 'a.0(2)': u64 - 97..98 '2': u32 + 65..73 'i as u64': u64 + 84..85 'b': u64 + 88..89 'a': S + 88..91 'a.0': fn(u32) -> u64 + 88..94 'a.0(4)': u64 + 92..93 '4': u32 + 100..101 'a': S + 100..103 'a.0': fn(u32) -> u64 + 100..106 'a.0(2)': u64 + 104..105 '2': u32 "#]], ); } diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index cd46573913965..0935b5ea51945 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -5,7 +5,7 @@ //! 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, ExpandProcMacroQuery, HygieneFrameQuery, + AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery, InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery, MacroExpandQuery, ParseMacroExpansionQuery, }; diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 8f019a81b2db4..253d62dafc60b 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -3,6 +3,8 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. +pub use hir_ty::diagnostics::{IncoherentImpl, IncorrectCase}; + use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -35,6 +37,7 @@ diagnostics![ InactiveCode, IncorrectCase, InvalidDeriveTarget, + IncoherentImpl, MacroError, MalformedDerive, MismatchedArgCount, @@ -220,5 +223,3 @@ pub struct NeedMut { pub struct UnusedMut { pub local: Local, } - -pub use hir_ty::diagnostics::IncorrectCase; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 25c07a2fbd3f9..35424feec8b29 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -85,10 +85,10 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase, - InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields, - MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro, + AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, + IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, + MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, + PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, }, @@ -604,11 +604,23 @@ impl Module { } } + let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); + for impl_def in self.impl_defs(db) { for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { emit_def_diagnostic(db, acc, diag); } + if inherent_impls.invalid_impls().contains(&impl_def.id) { + let loc = impl_def.id.lookup(db.upcast()); + let tree = loc.id.item_tree(db.upcast()); + let node = &tree[loc.id.value]; + let file_id = loc.id.file_id(); + let ast_id_map = db.ast_id_map(file_id); + + acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) + } + for item in impl_def.items(db) { let def: DefWithBody = match item { AssocItem::Function(it) => it.into(), @@ -3210,6 +3222,14 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Uint(UintTy::Usize))) } + pub fn is_float(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Float(_))) + } + + pub fn is_char(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Char)) + } + pub fn is_int_or_uint(&self) -> bool { match self.ty.kind(Interner) { TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)) => true, @@ -3224,6 +3244,13 @@ impl Type { } } + pub fn as_slice(&self) -> Option { + match &self.ty.kind(Interner) { + TyKind::Slice(ty) => Some(self.derived(ty.clone())), + _ => None, + } + } + pub fn strip_references(&self) -> Type { self.derived(self.ty.strip_references().clone()) } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 8bd905d0113a1..407ba6f65844e 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -15,7 +15,7 @@ use hir_def::{ AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId, }; use hir_expand::{ - db::AstDatabase, + db::ExpandDatabase, name::{known, AsName}, ExpansionInfo, MacroCallId, }; @@ -411,7 +411,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_record_field(field) } - pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option { + pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { self.imp.resolve_record_pat_field(field) } @@ -1201,7 +1201,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_field(self.db, field) } - fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option { + fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } @@ -1536,7 +1536,7 @@ impl<'db> SemanticsImpl<'db> { fn macro_call_to_macro_id( ctx: &mut SourceToDefCtx<'_, '_>, - db: &dyn AstDatabase, + db: &dyn ExpandDatabase, macro_call_id: MacroCallId, ) -> Option { let loc = db.lookup_intern_macro_call(macro_call_id); diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 133fa810d6613..c24d196e1b624 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -441,14 +441,17 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, field: &ast::RecordPatField, - ) -> Option { + ) -> Option<(Field, Type)> { let field_name = field.field_name()?.as_name(); let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; - Some(field.into()) + let (_, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; + let field_ty = + db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); + Some((field.into(), Type::new_with_resolver(db, &self.resolver, field_ty))) } pub(crate) fn resolve_macro_call( diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index eef037f98754a..0768389281ca3 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -1027,7 +1027,7 @@ fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option, ) -> Option<(FileId, GeneratedFunctionTarget)> { let file = module_source.file_id.original_file(db); diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 5ac18727c1960..28d815e81b49d 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -363,10 +363,10 @@ fn inline( .collect(); if function.self_param(sema.db).is_some() { - let this = || make::name_ref("this").syntax().clone_for_update(); + let this = || make::name_ref("this").syntax().clone_for_update().first_token().unwrap(); if let Some(self_local) = params[0].2.as_local(sema.db) { usages_for_locals(self_local) - .flat_map(|FileReference { name, range, .. }| match name { + .filter_map(|FileReference { name, range, .. }| match name { ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), _ => None, }) @@ -680,6 +680,42 @@ impl Foo { } } +fn main() { + let x = { + let ref this = Foo(3); + Foo(this.0 + 2) + }; +} +"#, + ); + } + + #[test] + fn generic_method_by_ref() { + check_assist( + inline_call, + r#" +struct Foo(u32); + +impl Foo { + fn add(&self, a: u32) -> Self { + Foo(self.0 + a) + } +} + +fn main() { + let x = Foo(3).add$0::(2); +} +"#, + r#" +struct Foo(u32); + +impl Foo { + fn add(&self, a: u32) -> Self { + Foo(self.0 + a) + } +} + fn main() { let x = { let ref this = Foo(3); diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 1361cdf00cc6d..a403d5bc672d2 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -46,7 +46,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( acc.add( AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", - ctx.selection_trimmed(), + replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(), |builder| { for (range, expr) in replacements { if let Some(expr) = expr { diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 4d489b62b5c88..8b07e29a5879f 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -265,7 +265,6 @@ mod handlers { inline_local_variable::inline_local_variable, inline_type_alias::inline_type_alias, inline_type_alias::inline_type_alias_uses, - inline_macro::inline_macro, introduce_named_generic::introduce_named_generic, introduce_named_lifetime::introduce_named_lifetime, invert_if::invert_if, @@ -286,7 +285,6 @@ mod handlers { raw_string::add_hash, raw_string::make_usual_string, raw_string::remove_hash, - remove_dbg::remove_dbg, remove_mut::remove_mut, remove_unused_param::remove_unused_param, remove_parentheses::remove_parentheses, @@ -335,6 +333,9 @@ mod handlers { generate_setter::generate_setter, generate_delegate_methods::generate_delegate_methods, generate_deref::generate_deref, + // + remove_dbg::remove_dbg, + inline_macro::inline_macro, // Are you sure you want to add new assist here, and not to the // sorted list above? ] diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index eb87d6c58262d..c3136f6df4b39 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -23,7 +23,7 @@ pub(crate) mod env_vars; use std::iter; -use hir::{known, ScopeDef}; +use hir::{known, ScopeDef, Variant}; use ide_db::{imports::import_assets::LocatedImport, SymbolKind}; use syntax::ast; @@ -537,17 +537,20 @@ fn enum_variants_with_paths( impl_: &Option, cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::Variant, hir::ModPath), ) { + let mut process_variant = |variant: Variant| { + let self_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), + ); + + cb(acc, ctx, variant, self_path); + }; + let variants = enum_.variants(ctx.db); if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) { - for &variant in &variants { - let self_path = hir::ModPath::from_segments( - hir::PathKind::Plain, - iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), - ); - cb(acc, ctx, variant, self_path); - } + variants.iter().for_each(|variant| process_variant(*variant)); } } diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 09ac57153ae8b..77246379e7bd9 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -415,7 +415,6 @@ fn foo(a: lib::A) { a.$0 } fn test_local_impls() { check( r#" -//- /lib.rs crate:lib pub struct A {} mod m { impl super::A { @@ -427,9 +426,8 @@ mod m { } } } -//- /main.rs crate:main deps:lib -fn foo(a: lib::A) { - impl lib::A { +fn foo(a: A) { + impl A { fn local_method(&self) {} } a.$0 diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 7dc29c3d5acad..8cbf89e9c3019 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -220,6 +220,8 @@ pub(super) struct PatternContext { /// The record pattern this name or ref is a field of pub(super) record_pat: Option, pub(super) impl_: Option, + /// List of missing variants in a match expr + pub(super) missing_variants: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index db0045aef6e0b..a94c404586b11 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1,7 +1,7 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{Semantics, Type, TypeInfo}; +use hir::{Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, @@ -353,7 +353,7 @@ fn expected_type_and_name( _ => ty, }; - loop { + let (ty, name) = loop { break match_ast! { match node { ast::LetStmt(it) => { @@ -385,9 +385,7 @@ fn expected_type_and_name( token.clone(), ).map(|ap| { let name = ap.ident().map(NameOrNameRef::Name); - - let ty = strip_refs(ap.ty); - (Some(ty), name) + (Some(ap.ty), name) }) .unwrap_or((None, None)) }, @@ -489,7 +487,8 @@ fn expected_type_and_name( }, } }; - } + }; + (ty.map(strip_refs), name) } fn classify_lifetime( @@ -1133,6 +1132,9 @@ fn pattern_context_for( pat: ast::Pat, ) -> PatternContext { let mut param_ctx = None; + + let mut missing_variants = vec![]; + let (refutability, has_type_ascription) = pat .syntax() @@ -1162,7 +1164,52 @@ fn pattern_context_for( })(); return (PatternRefutability::Irrefutable, has_type_ascription) }, - ast::MatchArm(_) => PatternRefutability::Refutable, + ast::MatchArm(match_arm) => { + let missing_variants_opt = match_arm + .syntax() + .parent() + .and_then(ast::MatchArmList::cast) + .and_then(|match_arm_list| { + match_arm_list + .syntax() + .parent() + .and_then(ast::MatchExpr::cast) + .and_then(|match_expr| { + let expr_opt = find_opt_node_in_file(&original_file, match_expr.expr()); + + expr_opt.and_then(|expr| { + sema.type_of_expr(&expr)? + .adjusted() + .autoderef(sema.db) + .find_map(|ty| match ty.as_adt() { + Some(hir::Adt::Enum(e)) => Some(e), + _ => None, + }).and_then(|enum_| { + Some(enum_.variants(sema.db)) + }) + }) + }).and_then(|variants| { + Some(variants.iter().filter_map(|variant| { + let variant_name = variant.name(sema.db).to_string(); + + let variant_already_present = match_arm_list.arms().any(|arm| { + arm.pat().and_then(|pat| { + let pat_already_present = pat.syntax().to_string().contains(&variant_name); + pat_already_present.then(|| pat_already_present) + }).is_some() + }); + + (!variant_already_present).then_some(variant.clone()) + }).collect::>()) + }) + }); + + if let Some(missing_variants_) = missing_variants_opt { + missing_variants = missing_variants_; + }; + + PatternRefutability::Refutable + }, ast::LetExpr(_) => PatternRefutability::Refutable, ast::ForExpr(_) => PatternRefutability::Irrefutable, _ => PatternRefutability::Irrefutable, @@ -1184,6 +1231,7 @@ fn pattern_context_for( ref_token, record_pat: None, impl_: fetch_immediate_impl(sema, original_file, pat.syntax()), + missing_variants, } } diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index a654a5db57445..82a1c10c5314f 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -411,3 +411,15 @@ fn main() { expect!["ty: i32, name: ?"], ); } + +#[test] +fn expected_type_ref_return_pos() { + check_expected_type_and_name( + r#" +fn f(thing: u32) -> &u32 { + &thin$0 +} +"#, + expect!["ty: u32, name: ?"], + ); +} diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 21b4bc2174bee..9225c91bebf51 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -37,7 +37,9 @@ pub(crate) fn render_struct_pat( let lookup = format_literal_lookup(name.as_str(), kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; - Some(build_completion(ctx, label, lookup, pat, strukt)) + let db = ctx.db(); + + Some(build_completion(ctx, label, lookup, pat, strukt, strukt.ty(db), false)) } pub(crate) fn render_variant_pat( @@ -52,6 +54,7 @@ pub(crate) fn render_variant_pat( let fields = variant.fields(ctx.db()); let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?; + let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db()); let (name, escaped_name) = match path { Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), @@ -81,7 +84,15 @@ pub(crate) fn render_variant_pat( } }; - Some(build_completion(ctx, label, lookup, pat, variant)) + Some(build_completion( + ctx, + label, + lookup, + pat, + variant, + enum_ty, + pattern_ctx.missing_variants.contains(&variant), + )) } fn build_completion( @@ -90,13 +101,22 @@ fn build_completion( lookup: SmolStr, pat: String, def: impl HasAttrs + Copy, + adt_ty: hir::Type, + // Missing in context of match statement completions + is_variant_missing: bool, ) -> CompletionItem { + let mut relevance = ctx.completion_relevance(); + + if is_variant_missing { + relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); + } + let mut item = CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label); item.set_documentation(ctx.docs(def)) .set_deprecated(ctx.is_deprecated(def)) .detail(&pat) .lookup_by(lookup) - .set_relevance(ctx.completion_relevance()); + .set_relevance(relevance); match ctx.snippet_cap() { Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), None => item.insert_text(pat), diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index ad9254e7f2ecf..c0e485c36fdd1 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -614,6 +614,7 @@ fn f(u: U) { check_empty( r#" +#![rustc_coherence_is_core] #[lang = "u32"] impl u32 { pub const MIN: Self = 0; diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index 328faaa060f04..65cefdb0856d2 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -46,6 +46,66 @@ fn foo(s: Struct) { ); } +#[test] +fn record_pattern_field_enum() { + check( + r#" +//- minicore:result +enum Baz { Foo, Bar } + +fn foo(baz: Baz) { + match baz { + Baz::Foo => (), + $0 + } +} +"#, + expect![[r#" + en Baz + en Result + md core + ev Err + ev Ok + bn Baz::Bar Baz::Bar$0 + bn Baz::Foo Baz::Foo$0 + bn Err(…) Err($1)$0 + bn Ok(…) Ok($1)$0 + kw mut + kw ref + "#]], + ); + + check( + r#" +//- minicore:result +enum Baz { Foo, Bar } + +fn foo(baz: Baz) { + use Baz::*; + match baz { + Foo => (), + $0 + } +} + "#, + expect![[r#" + en Baz + en Result + md core + ev Bar + ev Err + ev Foo + ev Ok + bn Bar Bar$0 + bn Err(…) Err($1)$0 + bn Foo Foo$0 + bn Ok(…) Ok($1)$0 + kw mut + kw ref + "#]], + ); +} + #[test] fn pattern_enum_variant() { check( diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index cb71c7b2bdef3..f8a6f6cd3ed06 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -608,6 +608,7 @@ fn f() { } //- /core.rs crate:core +#![rustc_coherence_is_core] #[lang = "u8"] impl u8 { pub const MAX: Self = 255; diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 244e99fe2e27d..ea1d9cc4919d3 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -71,7 +71,7 @@ impl RootDatabase { base_db::SourceRootQuery base_db::SourceRootCratesQuery - // AstDatabase + // ExpandDatabase hir::db::AstIdMapQuery hir::db::ParseMacroExpansionQuery hir::db::InternMacroCallQuery diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 1322f5228e8b9..4071c490b7fc3 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -327,7 +327,7 @@ impl NameClass { let pat_parent = ident_pat.syntax().parent(); if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) { if record_pat_field.name_ref().is_none() { - if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) { + if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) { return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field, @@ -483,6 +483,13 @@ impl NameRefClass { }, ast::RecordPatField(record_pat_field) => { sema.resolve_record_pat_field(&record_pat_field) + .map(|(field, ..)|field) + .map(Definition::Field) + .map(NameRefClass::Definition) + }, + ast::RecordExprField(record_expr_field) => { + sema.resolve_record_field(&record_expr_field) + .map(|(field, ..)|field) .map(Definition::Field) .map(NameRefClass::Definition) }, diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index ae120470047e4..b1df11bf91172 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -50,7 +50,7 @@ use base_db::{ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; use hir::{ - db::{AstDatabase, DefDatabase, HirDatabase}, + db::{DefDatabase, ExpandDatabase, HirDatabase}, symbols::FileSymbolKind, }; use stdx::hash::NoHashHashSet; @@ -68,7 +68,7 @@ pub type FxIndexMap = #[salsa::database( base_db::SourceDatabaseExtStorage, base_db::SourceDatabaseStorage, - hir::db::AstDatabaseStorage, + hir::db::ExpandDatabaseStorage, hir::db::DefDatabaseStorage, hir::db::HirDatabaseStorage, hir::db::InternDatabaseStorage, @@ -95,8 +95,8 @@ impl fmt::Debug for RootDatabase { } } -impl Upcast for RootDatabase { - fn upcast(&self) -> &(dyn AstDatabase + 'static) { +impl Upcast for RootDatabase { + fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { &*self } } diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs new file mode 100644 index 0000000000000..72af9ebfcbb62 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -0,0 +1,77 @@ +use hir::InFile; + +use crate::{Diagnostic, DiagnosticsContext, Severity}; + +// Diagnostic: incoherent-impl +// +// This diagnostic is triggered if the targe type of an impl is from a foreign crate. +pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic { + Diagnostic::new( + "incoherent-impl", + format!("cannot define inherent `impl` for foreign type"), + ctx.sema.diagnostics_display_range(InFile::new(d.file_id, d.impl_.clone().into())).range, + ) + .severity(Severity::Error) +} + +#[cfg(test)] +mod change_case { + use crate::tests::check_diagnostics; + + #[test] + fn primitive() { + check_diagnostics( + r#" + impl bool {} +//^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type +"#, + ); + } + + #[test] + fn primitive_rustc_allow_incoherent_impl() { + check_diagnostics( + r#" +impl bool { + #[rustc_allow_incoherent_impl] + fn falsch(self) -> Self { false } +} +"#, + ); + } + + #[test] + fn rustc_allow_incoherent_impl() { + check_diagnostics( + r#" +//- /lib.rs crate:foo +#[rustc_has_incoherent_inherent_impls] +pub struct S; +//- /main.rs crate:main deps:foo +impl foo::S { + #[rustc_allow_incoherent_impl] + fn func(self) {} +} +"#, + ); + check_diagnostics( + r#" +//- /lib.rs crate:foo +pub struct S; +//- /main.rs crate:main deps:foo + impl foo::S { #[rustc_allow_incoherent_impl] fn func(self) {} } +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type +"#, + ); + check_diagnostics( + r#" +//- /lib.rs crate:foo +#[rustc_has_incoherent_inherent_impls] +pub struct S; +//- /main.rs crate:main deps:foo + impl foo::S { fn func(self) {} } +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 6a78c08d44c19..db88bf7b9313d 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -1,4 +1,4 @@ -use hir::{db::AstDatabase, InFile}; +use hir::{db::ExpandDatabase, InFile}; use ide_db::{assists::Assist, defs::NameClass}; use syntax::AstNode; diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 14039087b3fd6..5c4327ff93413 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,6 +1,6 @@ use either::Either; use hir::{ - db::{AstDatabase, HirDatabase}, + db::{ExpandDatabase, HirDatabase}, known, AssocItem, HirDisplay, InFile, Type, }; use ide_db::{ diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index ea1ea5a216dfc..eb32db250656c 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1,4 +1,10 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use hir::db::ExpandDatabase; +use ide_db::{assists::Assist, source_change::SourceChange}; +use syntax::{ast, SyntaxNode}; +use syntax::{match_ast, AstNode}; +use text_edit::TextEdit; + +use crate::{fix, Diagnostic, DiagnosticsContext}; // Diagnostic: missing-unsafe // @@ -9,11 +15,83 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf "this operation is unsafe and requires an unsafe function or block", ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, ) + .with_fixes(fixes(ctx, d)) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option> { + // The fixit will not work correctly for macro expansions, so we don't offer it in that case. + if d.expr.file_id.is_macro() { + return None; + } + + let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; + let expr = d.expr.value.to_node(&root); + + let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?; + + let replacement = format!("unsafe {{ {} }}", node_to_add_unsafe_block.text()); + let edit = TextEdit::replace(node_to_add_unsafe_block.text_range(), replacement); + let source_change = + SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); + Some(vec![fix("add_unsafe", "Add unsafe block", source_change, expr.syntax().text_range())]) +} + +// Pick the first ancestor expression of the unsafe `expr` that is not a +// receiver of a method call, a field access, the left-hand side of an +// assignment, or a reference. As all of those cases would incur a forced move +// if wrapped which might not be wanted. That is: +// - `unsafe_expr.foo` -> `unsafe { unsafe_expr.foo }` +// - `unsafe_expr.foo.bar` -> `unsafe { unsafe_expr.foo.bar }` +// - `unsafe_expr.foo()` -> `unsafe { unsafe_expr.foo() }` +// - `unsafe_expr.foo.bar()` -> `unsafe { unsafe_expr.foo.bar() }` +// - `unsafe_expr += 1` -> `unsafe { unsafe_expr += 1 }` +// - `&unsafe_expr` -> `unsafe { &unsafe_expr }` +// - `&&unsafe_expr` -> `unsafe { &&unsafe_expr }` +fn pick_best_node_to_add_unsafe_block(unsafe_expr: &ast::Expr) -> Option { + // The `unsafe_expr` might be: + // - `ast::CallExpr`: call an unsafe function + // - `ast::MethodCallExpr`: call an unsafe method + // - `ast::PrefixExpr`: dereference a raw pointer + // - `ast::PathExpr`: access a static mut variable + for (node, parent) in + unsafe_expr.syntax().ancestors().zip(unsafe_expr.syntax().ancestors().skip(1)) + { + match_ast! { + match parent { + // If the `parent` is a `MethodCallExpr`, that means the `node` + // is the receiver of the method call, because only the receiver + // can be a direct child of a method call. The method name + // itself is not an expression but a `NameRef`, and an argument + // is a direct child of an `ArgList`. + ast::MethodCallExpr(_) => continue, + ast::FieldExpr(_) => continue, + ast::RefExpr(_) => continue, + ast::BinExpr(it) => { + // Check if the `node` is the left-hand side of an + // assignment, if so, we don't want to wrap it in an unsafe + // block, e.g. `unsafe_expr += 1` + let is_left_hand_side_of_assignment = { + if let Some(ast::BinaryOp::Assignment { .. }) = it.op_kind() { + it.lhs().map(|lhs| lhs.syntax().text_range().contains_range(node.text_range())).unwrap_or(false) + } else { + false + } + }; + if !is_left_hand_side_of_assignment { + return Some(node); + } + }, + _ => { return Some(node); } + + } + } + } + None } #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] fn missing_unsafe_diagnostic_with_raw_ptr() { @@ -23,7 +101,7 @@ fn main() { let x = &5 as *const usize; unsafe { let y = *x; } let z = *x; -} //^^ error: this operation is unsafe and requires an unsafe function or block +} //^^💡 error: this operation is unsafe and requires an unsafe function or block "#, ) } @@ -48,9 +126,9 @@ unsafe fn unsafe_fn() { fn main() { unsafe_fn(); - //^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block + //^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block HasUnsafe.unsafe_fn(); - //^^^^^^^^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block + //^^^^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block unsafe { unsafe_fn(); HasUnsafe.unsafe_fn(); @@ -72,7 +150,7 @@ static mut STATIC_MUT: Ty = Ty { a: 0 }; fn main() { let x = STATIC_MUT.a; - //^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block + //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block unsafe { let x = STATIC_MUT.a; } @@ -94,9 +172,298 @@ extern "rust-intrinsic" { fn main() { let _ = bitreverse(12); let _ = floorf32(12.0); - //^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block + //^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block } "#, ); } + + #[test] + fn add_unsafe_block_when_dereferencing_a_raw_pointer() { + check_fix( + r#" +fn main() { + let x = &5 as *const usize; + let z = *x$0; +} +"#, + r#" +fn main() { + let x = &5 as *const usize; + let z = unsafe { *x }; +} +"#, + ); + } + + #[test] + fn add_unsafe_block_when_calling_unsafe_function() { + check_fix( + r#" +unsafe fn func() { + let x = &5 as *const usize; + let z = *x; +} +fn main() { + func$0(); +} +"#, + r#" +unsafe fn func() { + let x = &5 as *const usize; + let z = *x; +} +fn main() { + unsafe { func() }; +} +"#, + ) + } + + #[test] + fn add_unsafe_block_when_calling_unsafe_method() { + check_fix( + r#" +struct S(usize); +impl S { + unsafe fn func(&self) { + let x = &self.0 as *const usize; + let z = *x; + } +} +fn main() { + let s = S(5); + s.func$0(); +} +"#, + r#" +struct S(usize); +impl S { + unsafe fn func(&self) { + let x = &self.0 as *const usize; + let z = *x; + } +} +fn main() { + let s = S(5); + unsafe { s.func() }; +} +"#, + ) + } + + #[test] + fn add_unsafe_block_when_accessing_mutable_static() { + check_fix( + r#" +struct Ty { + a: u8, +} + +static mut STATIC_MUT: Ty = Ty { a: 0 }; + +fn main() { + let x = STATIC_MUT$0.a; +} +"#, + r#" +struct Ty { + a: u8, +} + +static mut STATIC_MUT: Ty = Ty { a: 0 }; + +fn main() { + let x = unsafe { STATIC_MUT.a }; +} +"#, + ) + } + + #[test] + fn add_unsafe_block_when_calling_unsafe_intrinsic() { + check_fix( + r#" +extern "rust-intrinsic" { + pub fn floorf32(x: f32) -> f32; +} + +fn main() { + let _ = floorf32$0(12.0); +} +"#, + r#" +extern "rust-intrinsic" { + pub fn floorf32(x: f32) -> f32; +} + +fn main() { + let _ = unsafe { floorf32(12.0) }; +} +"#, + ) + } + + #[test] + fn unsafe_expr_as_a_receiver_of_a_method_call() { + check_fix( + r#" +unsafe fn foo() -> String { + "string".to_string() +} + +fn main() { + foo$0().len(); +} +"#, + r#" +unsafe fn foo() -> String { + "string".to_string() +} + +fn main() { + unsafe { foo().len() }; +} +"#, + ) + } + + #[test] + fn unsafe_expr_as_an_argument_of_a_method_call() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let mut v = vec![]; + v.push(STATIC_MUT$0); +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let mut v = vec![]; + v.push(unsafe { STATIC_MUT }); +} +"#, + ) + } + + #[test] + fn unsafe_expr_as_left_hand_side_of_assignment() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + STATIC_MUT$0 = 1; +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + unsafe { STATIC_MUT = 1 }; +} +"#, + ) + } + + #[test] + fn unsafe_expr_as_right_hand_side_of_assignment() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x; + x = STATIC_MUT$0; +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x; + x = unsafe { STATIC_MUT }; +} +"#, + ) + } + + #[test] + fn unsafe_expr_in_binary_plus() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = STATIC_MUT$0 + 1; +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = unsafe { STATIC_MUT } + 1; +} +"#, + ) + } + + #[test] + fn ref_to_unsafe_expr() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = &STATIC_MUT$0; +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = unsafe { &STATIC_MUT }; +} +"#, + ) + } + + #[test] + fn ref_ref_to_unsafe_expr() { + check_fix( + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = &&STATIC_MUT$0; +} +"#, + r#" +static mut STATIC_MUT: u8 = 0; + +fn main() { + let x = unsafe { &&STATIC_MUT }; +} +"#, + ) + } + + #[test] + fn unsafe_expr_in_macro_call() { + check_no_fix( + r#" +unsafe fn foo() -> u8 { + 0 +} + +fn main() { + let x = format!("foo: {}", foo$0()); +} + "#, + ) + } } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 84189a5d560f9..96470265d11d1 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -505,6 +505,30 @@ fn main() { ); } + #[test] + fn initialization_is_not_mutation_in_loop() { + check_diagnostics( + r#" +fn main() { + let a; + loop { + let c @ ( + mut b, + //^^^^^ 💡 weak: variable does not need to be mutable + mut d + //^^^^^ 💡 weak: variable does not need to be mutable + ); + a = 1; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + b = 1; + c = (2, 3); + d = 3; + } +} +"#, + ); + } + #[test] fn function_arguments_are_initialized() { check_diagnostics( diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 8da04e628d670..24c521ed1a8a4 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,4 +1,4 @@ -use hir::{db::AstDatabase, HasSource, HirDisplay, Semantics}; +use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics}; use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; use syntax::{ ast::{self, edit::IndentLevel, make}, diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs index e630ae36866d3..be83ad6aaadaa 100644 --- a/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/crates/ide-diagnostics/src/handlers/private_field.rs @@ -62,6 +62,26 @@ mod module { fn main(s: module::Struct) { s.field; } +"#, + ); + } + + #[test] + fn block_module_madness() { + check_diagnostics( + r#" +fn main() { + let strukt = { + use crate as ForceParentBlockDefMap; + { + pub struct Struct { + field: (), + } + Struct { field: () } + } + }; + strukt.field; +} "#, ); } diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index a0c276cc3328b..9b1c65983e615 100644 --- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -1,4 +1,4 @@ -use hir::{db::AstDatabase, InFile}; +use hir::{db::ExpandDatabase, InFile}; use ide_db::source_change::SourceChange; use syntax::{ ast::{self, HasArgList}, diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index b57a13e53e6b6..4abc25a28fbc0 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::AstDatabase, HirDisplay, InFile, Type}; +use hir::{db::ExpandDatabase, HirDisplay, InFile, Type}; use ide_db::{famous_defs::FamousDefs, source_change::SourceChange}; use syntax::{ ast::{self, BlockExpr, ExprStmt}, diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 7de03416e5625..cefa74e523e8f 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -1,4 +1,4 @@ -use hir::{db::AstDatabase, HirDisplay, InFile}; +use hir::{db::ExpandDatabase, HirDisplay, InFile}; use ide_db::{ assists::{Assist, AssistId, AssistKind}, base_db::FileRange, diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 4b0e64cb896d5..f3ec6efa75215 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -1,4 +1,4 @@ -use hir::{db::AstDatabase, HirDisplay}; +use hir::{db::ExpandDatabase, HirDisplay}; use ide_db::{ assists::{Assist, AssistId, AssistKind}, base_db::FileRange, diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 91395f1d841ad..94614f11c3349 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -1,4 +1,4 @@ -use hir::db::AstDatabase; +use hir::db::ExpandDatabase; use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit}; use itertools::Itertools; use syntax::AstNode; diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index f6c9b79c30c3c..71f136b8c9030 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -29,6 +29,7 @@ mod handlers { pub(crate) mod break_outside_of_loop; pub(crate) mod expected_function; pub(crate) mod inactive_code; + pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; pub(crate) mod invalid_derive_target; pub(crate) mod macro_error; @@ -254,6 +255,7 @@ pub fn diagnostics( 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::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 190ab80ba0ff3..a1a119629a94e 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -297,6 +297,7 @@ impl Foo {} //- /lib.rs crate:main deps:core fn foo(_: bool$0) {{}} //- /libcore.rs crate:core +#![rustc_coherence_is_core] #[lang = "bool"] impl bool {} //^^^^ diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 55cdb3200eac9..6d2d0bd635165 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -55,7 +55,7 @@ pub(crate) fn goto_type_definition( ty } else { let record_field = ast::RecordPatField::for_field_name_ref(&it)?; - sema.resolve_record_pat_field(&record_field)?.ty(db) + sema.resolve_record_pat_field(&record_field)?.1 } }, _ => return None, diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 729780fa0c919..46505b3044109 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -31,19 +31,31 @@ pub(super) fn hints( return None; } - // These inherit from the inner expression which would result in duplicate hints - if let ast::Expr::ParenExpr(_) - | ast::Expr::IfExpr(_) - | ast::Expr::BlockExpr(_) - | ast::Expr::MatchExpr(_) = expr - { + // ParenExpr resolve to their contained expressions HIR so they will dupe these hints + if let ast::Expr::ParenExpr(_) = expr { return None; } + if let ast::Expr::BlockExpr(b) = expr { + if !b.is_standalone() { + return None; + } + } let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr { + if let [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), source: _, target }] = + &*adjustments + { + // Don't show unnecessary reborrows for these, they will just repeat the inner ones again + if source == target { + return None; + } + } + } + let (postfix, needs_outer_parens, needs_inner_parens) = mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode); @@ -67,6 +79,7 @@ pub(super) fn hints( for Adjustment { source, target, kind } in iter { if source == target { + cov_mark::hit!(same_type_adjustment); continue; } @@ -251,7 +264,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn +//- minicore: coerce_unsized, fn, eq fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -332,7 +345,7 @@ fn main() { loop {} //^^^^^^^ }; - let _: &mut [u32] = match () { () => &mut [] } + let _: &mut [u32] = match () { () => &mut [] }; //^^^^^^^ //^^^^^^^&mut $ //^^^^^^^* @@ -341,6 +354,12 @@ fn main() { //^^^^^^^^^^ //^^^^^^^^^^&mut $ //^^^^^^^^^^* + () == (); + // ^^& + // ^^& + (()) == {()}; + // ^^& + // ^^^^& } #[derive(Copy, Clone)] @@ -363,7 +382,7 @@ impl Struct { ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn +//- minicore: coerce_unsized, fn, eq fn main() { Struct.consume(); @@ -419,7 +438,7 @@ fn main() { loop {} //^^^^^^^. }; - let _: &mut [u32] = match () { () => &mut [] } + let _: &mut [u32] = match () { () => &mut [] }; //^^^^^^^( //^^^^^^^) //^^^^^^^.* @@ -432,6 +451,12 @@ fn main() { //^^^^^^^^^^.* //^^^^^^^^^^.&mut //^^^^^^^^^^. + () == (); + // ^^.& + // ^^.& + (()) == {()}; + // ^^.& + // ^^^^.& } #[derive(Copy, Clone)] @@ -499,6 +524,7 @@ fn main() { #[test] fn never_to_never_is_never_shown() { + cov_mark::check!(same_type_adjustment); check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 0a7513e465a57..1e1771259b1ba 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -435,7 +435,7 @@ fn main() { file_id: FileId( 1, ), - range: 3386..3394, + range: 3415..3423, }, ), tooltip: "", @@ -448,7 +448,7 @@ fn main() { file_id: FileId( 1, ), - range: 3418..3422, + range: 3447..3451, }, ), tooltip: "", @@ -468,7 +468,7 @@ fn main() { file_id: FileId( 1, ), - range: 3386..3394, + range: 3415..3423, }, ), tooltip: "", @@ -481,7 +481,7 @@ fn main() { file_id: FileId( 1, ), - range: 3418..3422, + range: 3447..3451, }, ), tooltip: "", @@ -501,7 +501,7 @@ fn main() { file_id: FileId( 1, ), - range: 3386..3394, + range: 3415..3423, }, ), tooltip: "", @@ -514,7 +514,7 @@ fn main() { file_id: FileId( 1, ), - range: 3418..3422, + range: 3447..3451, }, ), tooltip: "", diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 2c08c457b338c..4b2c139f6f455 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -16,7 +16,7 @@ use stdx::format_to; use syntax::{ algo, ast::{self, HasArgList}, - match_ast, AstNode, Direction, SyntaxToken, TextRange, TextSize, + match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize, }; use crate::RootDatabase; @@ -102,6 +102,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio } return signature_help_for_record_lit(&sema, record, token); }, + ast::RecordPat(record) => { + let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_record_pat(&sema, record, token); + }, + ast::TupleStructPat(tuple_pat) => { + let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token); + }, _ => (), } } @@ -346,10 +360,111 @@ fn signature_help_for_record_lit( record: ast::RecordExpr, token: SyntaxToken, ) -> Option { - let active_parameter = record - .record_expr_field_list()? + signature_help_for_record_( + sema, + record.record_expr_field_list()?.syntax().children_with_tokens(), + &record.path()?, + record + .record_expr_field_list()? + .fields() + .filter_map(|field| sema.resolve_record_field(&field)) + .map(|(field, _, ty)| (field, ty)), + token, + ) +} + +fn signature_help_for_record_pat( + sema: &Semantics<'_, RootDatabase>, + record: ast::RecordPat, + token: SyntaxToken, +) -> Option { + signature_help_for_record_( + sema, + record.record_pat_field_list()?.syntax().children_with_tokens(), + &record.path()?, + record + .record_pat_field_list()? + .fields() + .filter_map(|field| sema.resolve_record_pat_field(&field)), + token, + ) +} + +fn signature_help_for_tuple_struct_pat( + sema: &Semantics<'_, RootDatabase>, + pat: ast::TupleStructPat, + token: SyntaxToken, +) -> Option { + let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_))); + let is_left_of_rest_pat = + rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); + + let mut res = SignatureHelp { + doc: None, + signature: String::new(), + parameters: vec![], + active_parameter: None, + }; + + let db = sema.db; + let path_res = sema.resolve_path(&pat.path()?)?; + let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res { + let en = variant.parent_enum(db); + + res.doc = en.docs(db).map(|it| it.into()); + format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db)); + variant.fields(db) + } else { + let adt = match path_res { + PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?, + PathResolution::Def(ModuleDef::Adt(adt)) => adt, + _ => return None, + }; + + match adt { + hir::Adt::Struct(it) => { + res.doc = it.docs(db).map(|it| it.into()); + format_to!(res.signature, "struct {} (", it.name(db)); + it.fields(db) + } + _ => return None, + } + }; + let commas = pat .syntax() .children_with_tokens() + .filter_map(syntax::NodeOrToken::into_token) + .filter(|t| t.kind() == syntax::T![,]); + res.active_parameter = Some(if is_left_of_rest_pat { + commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count() + } else { + let n_commas = commas + .collect::>() + .into_iter() + .rev() + .take_while(|t| t.text_range().start() > token.text_range().start()) + .count(); + fields.len().saturating_sub(1).saturating_sub(n_commas) + }); + + let mut buf = String::new(); + for ty in fields.into_iter().map(|it| it.ty(db)) { + format_to!(buf, "{}", ty.display_truncated(db, Some(20))); + res.push_call_param(&buf); + buf.clear(); + } + res.signature.push_str(")"); + Some(res) +} + +fn signature_help_for_record_( + sema: &Semantics<'_, RootDatabase>, + field_list_children: SyntaxElementChildren, + path: &ast::Path, + fields2: impl Iterator, + token: SyntaxToken, +) -> Option { + let active_parameter = field_list_children .filter_map(syntax::NodeOrToken::into_token) .filter(|t| t.kind() == syntax::T![,]) .take_while(|t| t.text_range().start() <= token.text_range().start()) @@ -365,7 +480,7 @@ fn signature_help_for_record_lit( let fields; let db = sema.db; - let path_res = sema.resolve_path(&record.path()?)?; + let path_res = sema.resolve_path(path)?; if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res { fields = variant.fields(db); let en = variant.parent_enum(db); @@ -397,8 +512,7 @@ fn signature_help_for_record_lit( let mut fields = fields.into_iter().map(|field| (field.name(db), Some(field))).collect::>(); let mut buf = String::new(); - for field in record.record_expr_field_list()?.fields() { - let Some((field, _, ty)) = sema.resolve_record_field(&field) else { continue }; + for (field, ty) in fields2 { let name = field.name(db); format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20))); res.push_record_field(&buf); @@ -439,6 +553,7 @@ mod tests { (database, FilePosition { file_id, offset }) } + #[track_caller] fn check(ra_fixture: &str, expect: Expect) { let fixture = format!( r#" @@ -890,6 +1005,119 @@ fn main() { ); } + #[test] + fn tuple_struct_pat() { + check( + r#" +/// A cool tuple struct +struct S(u32, i32); +fn main() { + let S(0, $0); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32) + --- ^^^ + "#]], + ); + } + + #[test] + fn tuple_struct_pat_rest() { + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16); +fn main() { + let S(0, .., $0); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16) + --- --- --- ^^^ + "#]], + ); + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16, u8); +fn main() { + let S(0, .., $0, 0); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16, u8) + --- --- --- ^^^ -- + "#]], + ); + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16); +fn main() { + let S($0, .., 1); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16) + ^^^ --- --- --- + "#]], + ); + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16, u8); +fn main() { + let S(1, .., 1, $0, 2); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16, u8) + --- --- --- ^^^ -- + "#]], + ); + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16); +fn main() { + let S(1, $0.., 1); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16) + --- ^^^ --- --- + "#]], + ); + check( + r#" +/// A cool tuple struct +struct S(u32, i32, f32, u16); +fn main() { + let S(1, ..$0, 1); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S (u32, i32, f32, u16) + --- ^^^ --- --- + "#]], + ); + } + #[test] fn generic_struct() { check( @@ -1550,6 +1778,29 @@ impl S { ); } + #[test] + fn record_pat() { + check( + r#" +struct Strukt { + t: T, + u: U, + unit: (), +} +fn f() { + let Strukt { + u: 0, + $0 + } +} +"#, + expect![[r#" + struct Strukt { u: i32, t: T, unit: () } + ------ ^^^^ -------- + "#]], + ); + } + #[test] fn test_enum_in_nested_method_in_lambda() { check( diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index abcefffa23f01..5f4977886f6eb 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -431,14 +431,15 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker { fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) { while !p.at(EOF) && !p.at(ket) { - if !p.at_ts(PAT_TOP_FIRST) { - p.error("expected a pattern"); - break; - } - pattern_top(p); - if !p.at(ket) { - p.expect(T![,]); + if !p.at(T![,]) { + if p.at_ts(PAT_TOP_FIRST) { + p.error(format!("expected {:?}, got {:?}", T![,], p.current())); + } else { + break; + } + } else { + p.bump(T![,]); } } } diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 6df1273edd65d..4e5d640f175e4 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -429,8 +429,9 @@ impl WorkspaceBuildScripts { 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) + if let Some((_, path)) = proc_macro_dylibs + .iter() + .find(|(name, _)| *name.trim_start_matches("lib") == package.name) { bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); } diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 732adc50b5001..01162b1a8ba0c 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -50,7 +50,7 @@ impl ops::Index for CargoWorkspace { /// Describes how to set the rustc source directory. #[derive(Clone, Debug, PartialEq, Eq)] -pub enum RustcSource { +pub enum RustLibSource { /// Explicit path for the rustc source directory. Path(AbsPathBuf), /// Try to automatically detect where the rustc source directory is. @@ -95,10 +95,10 @@ pub struct CargoConfig { /// rustc target pub target: Option, /// Sysroot loading behavior - pub sysroot: Option, + pub sysroot: Option, pub sysroot_src: Option, /// rustc private crate source - pub rustc_source: Option, + pub rustc_source: Option, /// crates to disable `#[cfg(test)]` on pub unset_test_crates: UnsetTestCrates, /// Invoke `cargo check` through the RUSTC_WRAPPER. diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 9b6a71db81145..70cb71ae3bde8 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -44,7 +44,7 @@ pub use crate::{ build_scripts::WorkspaceBuildScripts, cargo_workspace::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustcSource, Target, TargetData, TargetKind, UnsetTestCrates, + RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 749eee531eed1..3754accbb03d8 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -24,8 +24,8 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr let project_workspace = ProjectWorkspace::Cargo { cargo: cargo_workspace, build_scripts: WorkspaceBuildScripts::default(), - sysroot: None, - rustc: None, + sysroot: Err(None), + rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides, toolchain: None, @@ -37,7 +37,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr fn load_rust_project(file: &str) -> CrateGraph { let data = get_test_json_file(file); let project = rooted_project_json(data); - let sysroot = Some(get_fake_sysroot()); + let sysroot = Ok(get_fake_sysroot()); let project_workspace = ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new() }; to_crate_graph(project_workspace) } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index faa6816fdc203..d1e53e12eebb5 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -17,7 +17,7 @@ use stdx::{always, hash::NoHashHashMap}; use crate::{ build_scripts::BuildScriptOutput, - cargo_workspace::{DepKind, PackageData, RustcSource}, + cargo_workspace::{DepKind, PackageData, RustLibSource}, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, @@ -69,8 +69,8 @@ pub enum ProjectWorkspace { Cargo { cargo: CargoWorkspace, build_scripts: WorkspaceBuildScripts, - sysroot: Option, - rustc: Option<(CargoWorkspace, WorkspaceBuildScripts)>, + sysroot: Result>, + rustc: Result<(CargoWorkspace, WorkspaceBuildScripts), Option>, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. /// @@ -82,7 +82,7 @@ pub enum ProjectWorkspace { target_layout: Result, }, /// Project workspace was manually specified using a `rust-project.json` file. - Json { project: ProjectJson, sysroot: Option, rustc_cfg: Vec }, + Json { project: ProjectJson, sysroot: Result>, rustc_cfg: Vec }, // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning. // That's not the end user experience we should strive for. // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working. @@ -93,7 +93,11 @@ pub enum ProjectWorkspace { // // /// Project with a set of disjoint files, not belonging to any particular workspace. /// Backed by basic sysroot crates for basic completion and highlighting. - DetachedFiles { files: Vec, sysroot: Option, rustc_cfg: Vec }, + DetachedFiles { + files: Vec, + sysroot: Result>, + rustc_cfg: Vec, + }, } impl fmt::Debug for ProjectWorkspace { @@ -113,7 +117,7 @@ impl fmt::Debug for ProjectWorkspace { .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) .field("n_packages", &cargo.packages().len()) - .field("sysroot", &sysroot.is_some()) + .field("sysroot", &sysroot.is_ok()) .field( "n_rustc_compiler_crates", &rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()), @@ -126,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace { ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); - if let Some(sysroot) = sysroot { + if let Ok(sysroot) = sysroot { debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); } debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); @@ -135,7 +139,7 @@ impl fmt::Debug for ProjectWorkspace { ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f .debug_struct("DetachedFiles") .field("n_files", &files.len()) - .field("sysroot", &sysroot.is_some()) + .field("sysroot", &sysroot.is_ok()) .field("n_rustc_cfg", &rustc_cfg.len()) .finish(), } @@ -191,93 +195,81 @@ impl ProjectWorkspace { let cargo = CargoWorkspace::new(meta); let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustcSource::Path(path)), None) => { - match Sysroot::with_sysroot_dir(path.clone()) { - Ok(it) => Some(it), - Err(e) => { - tracing::error!(%e, "Failed to find sysroot at {}.", path.display()); - None - } - } + (Some(RustLibSource::Path(path)), None) => { + Sysroot::with_sysroot_dir(path.clone()).map_err(|e| { + Some(format!("Failed to find sysroot at {}:{e}", path.display())) + }) } - (Some(RustcSource::Discover), None) => { - match Sysroot::discover(cargo_toml.parent(), &config.extra_env) { - Ok(it) => Some(it), - Err(e) => { - tracing::error!( - %e, - "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", - cargo_toml.display() - ); - None - } - } + (Some(RustLibSource::Discover), None) => { + Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| { + Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display())) + }) } - (Some(RustcSource::Path(sysroot)), Some(sysroot_src)) => { - Some(Sysroot::load(sysroot.clone(), sysroot_src.clone())) + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { + Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone())) } - (Some(RustcSource::Discover), Some(sysroot_src)) => { - match Sysroot::discover_with_src_override( + (Some(RustLibSource::Discover), Some(sysroot_src)) => { + Sysroot::discover_with_src_override( cargo_toml.parent(), &config.extra_env, sysroot_src.clone(), - ) { - Ok(it) => Some(it), - Err(e) => { - tracing::error!( - %e, - "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", - cargo_toml.display() - ); - None - } - } + ).map_err(|e| { + Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display())) + }) } - (None, _) => None, + (None, _) => Err(None), }; - if let Some(sysroot) = &sysroot { + if let Ok(sysroot) = &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.as_ref().and_then(Sysroot::discover_rustc) + Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) + .map_err(|p| { + Some(format!("rustc source path is not absolute: {}", p.display())) + }), + Some(RustLibSource::Discover) => { + sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| { + Some(format!("Failed to discover rustc source for sysroot.")) + }) } - None => None, + None => Err(None), }; - let rustc = match rustc_dir { - 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 - } + let rustc = rustc_dir.and_then(|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(), + &CargoConfig { + features: crate::CargoFeatures::default(), + ..config.clone() + }, + progress, + ) { + Ok(meta) => { + let workspace = CargoWorkspace::new(meta); + let buildscripts = WorkspaceBuildScripts::rustc_crates( + &workspace, + cargo_toml.parent(), + &config.extra_env, + ); + Ok((workspace, buildscripts)) + } + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {}", + rustc_dir.display() + ); + Err(Some(format!( + "Failed to read Cargo metadata from rustc source at {}: {e}", + rustc_dir.display()) + )) } } - None => None, - }; + }); let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); @@ -313,12 +305,12 @@ impl ProjectWorkspace { extra_env: &FxHashMap, ) -> ProjectWorkspace { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { - (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)), + (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)), (Some(sysroot), None) => { // assume sysroot is structured like rustup's and guess `sysroot_src` let sysroot_src = sysroot.join("lib").join("rustlib").join("src").join("rust").join("library"); - Some(Sysroot::load(sysroot, sysroot_src)) + Ok(Sysroot::load(sysroot, sysroot_src)) } (None, Some(sysroot_src)) => { // assume sysroot is structured like rustup's and guess `sysroot` @@ -326,11 +318,11 @@ impl ProjectWorkspace { for _ in 0..5 { sysroot.pop(); } - Some(Sysroot::load(sysroot, sysroot_src)) + Ok(Sysroot::load(sysroot, sysroot_src)) } - (None, None) => None, + (None, None) => Err(None), }; - if let Some(sysroot) = &sysroot { + if let Ok(sysroot) = &sysroot { tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); } @@ -343,33 +335,23 @@ impl ProjectWorkspace { config: &CargoConfig, ) -> Result { let sysroot = match &config.sysroot { - Some(RustcSource::Path(path)) => match Sysroot::with_sysroot_dir(path.clone()) { - Ok(it) => Some(it), - Err(e) => { - tracing::error!(%e, "Failed to find sysroot at {}.", path.display()); - None - } - }, - Some(RustcSource::Discover) => { + Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone()) + .map_err(|e| Some(format!("Failed to find sysroot at {}:{e}", path.display()))), + Some(RustLibSource::Discover) => { let dir = &detached_files .first() .and_then(|it| it.parent()) .ok_or_else(|| format_err!("No detached files to load"))?; - match Sysroot::discover(dir, &config.extra_env) { - Ok(it) => Some(it), - Err(e) => { - tracing::error!( - %e, - "Failed to find sysroot for {}. Is rust-src installed?", - dir.display() - ); - None - } - } + Sysroot::discover(dir, &config.extra_env).map_err(|e| { + Some(format!( + "Failed to find sysroot for {}. Is rust-src installed? {e}", + dir.display() + )) + }) } - None => None, + None => Err(None), }; - if let Some(sysroot) = &sysroot { + if let Ok(sysroot) = &sysroot { tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); } let rustc_cfg = rustc_cfg::get(None, None, &Default::default()); @@ -450,10 +432,18 @@ impl ProjectWorkspace { } } + pub fn workspace_definition_path(&self) -> Option<&AbsPath> { + match self { + ProjectWorkspace::Cargo { cargo, .. } => Some(cargo.workspace_root()), + ProjectWorkspace::Json { project, .. } => Some(project.path()), + ProjectWorkspace::DetachedFiles { .. } => None, + } + } + pub fn find_sysroot_proc_macro_srv(&self) -> Option { match self { - ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. } - | ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => { + ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } + | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => { let standalone_server_name = format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); ["libexec", "lib"] @@ -469,7 +459,7 @@ impl ProjectWorkspace { /// The return type contains the path and whether or not /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { - let mk_sysroot = |sysroot: Option<&Sysroot>, project_root: Option<&AbsPath>| { + let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| { sysroot.map(|sysroot| PackageRoot { // mark the sysroot as mutable if it is located inside of the project is_local: project_root @@ -592,7 +582,7 @@ impl ProjectWorkspace { load_proc_macro, load, project, - sysroot.as_ref(), + sysroot.as_ref().ok(), extra_env, Err("rust-project.json projects have no target layout set".into()), ), @@ -608,9 +598,9 @@ impl ProjectWorkspace { } => cargo_to_crate_graph( load_proc_macro, load, - rustc, + rustc.as_ref().ok(), cargo, - sysroot.as_ref(), + sysroot.as_ref().ok(), rustc_cfg.clone(), cfg_overrides, build_scripts, @@ -624,7 +614,7 @@ impl ProjectWorkspace { rustc_cfg.clone(), load, files, - sysroot, + sysroot.as_ref().ok(), Err("detached file projects have no target layout set".into()), ) } @@ -786,7 +776,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<(CargoWorkspace, WorkspaceBuildScripts)>, + rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: Option<&Sysroot>, rustc_cfg: Vec, @@ -932,7 +922,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, build_scripts)) = rustc { + if let Some((rustc_workspace, rustc_build_scripts)) = rustc { handle_rustc_crates( &mut crate_graph, &mut pkg_to_lib_crate, @@ -945,7 +935,13 @@ fn cargo_to_crate_graph( &pkg_crates, &cfg_options, override_cfg, - build_scripts, + if rustc_workspace.workspace_root() == cargo.workspace_root() { + // the rustc workspace does not use the installed toolchain's proc-macro server + // so we need to make sure we don't use the pre compiled proc-macros there either + build_scripts + } else { + rustc_build_scripts + }, target_layout, ); } @@ -957,7 +953,7 @@ fn detached_files_to_crate_graph( rustc_cfg: Vec, load: &mut dyn FnMut(&AbsPath) -> Option, detached_files: &[AbsPathBuf], - sysroot: &Option, + sysroot: Option<&Sysroot>, target_layout: TargetLayoutLoadResult, ) -> CrateGraph { let _p = profile::span("detached_files_to_crate_graph"); diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index e8c10927d62c4..6ce1de5d32bca 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -7,7 +7,7 @@ use std::{ }; use hir::{ - db::{AstDatabase, DefDatabase, HirDatabase}, + db::{DefDatabase, ExpandDatabase, HirDatabase}, AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef, }; use hir_def::{ @@ -24,7 +24,7 @@ use ide_db::base_db::{ use itertools::Itertools; use oorandom::Rand32; use profile::{Bytes, StopWatch}; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use rayon::prelude::*; use rustc_hash::FxHashSet; use stdx::format_to; @@ -57,7 +57,7 @@ impl flags::AnalysisStats { let mut cargo_config = CargoConfig::default(); cargo_config.sysroot = match self.no_sysroot { true => None, - false => Some(RustcSource::Discover), + false => Some(RustLibSource::Discover), }; let no_progress = &|_| (); diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 0721d486ef1f9..4006d023def52 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -1,7 +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 project_model::{CargoConfig, RustLibSource}; use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, Module}; @@ -16,7 +16,7 @@ use crate::cli::{ impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = Some(RustcSource::Discover); + cargo_config.sysroot = Some(RustLibSource::Discover); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 9b5451496c6c5..7f5d084496714 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/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, RustcSource}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use vfs::{AbsPathBuf, Vfs}; use crate::cli::load_cargo::ProcMacroServerChoice; @@ -290,7 +290,7 @@ impl flags::Lsif { eprintln!("Generating LSIF started..."); let now = Instant::now(); let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = Some(RustcSource::Discover); + cargo_config.sysroot = Some(RustLibSource::Discover); let no_progress = &|_| (); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index df5c26cf77a94..3e5e40750e9ca 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -15,7 +15,7 @@ use ide::{ TokenStaticData, }; use ide_db::LineIndexDatabase; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; use scip::types as scip_types; use std::env; @@ -30,7 +30,7 @@ impl flags::Scip { eprintln!("Generating SCIP start..."); let now = Instant::now(); let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = Some(RustcSource::Discover); + cargo_config.sysroot = Some(RustLibSource::Discover); let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}")); let load_cargo_config = LoadCargoConfig { diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 35a874f89207b..82a769347df04 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/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, RustcSource}; +use project_model::{CargoConfig, RustLibSource}; use crate::cli::{ flags, @@ -13,7 +13,7 @@ impl flags::Ssr { pub fn run(self) -> Result<()> { use ide_db::base_db::SourceDatabaseExt; let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = Some(RustcSource::Discover); + cargo_config.sysroot = Some(RustLibSource::Discover); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 75233dbb2abec..c35cce103fab6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -22,7 +22,7 @@ use ide_db::{ use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{ - CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, + CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, UnsetTestCrates, }; use rustc_hash::{FxHashMap, FxHashSet}; @@ -272,7 +272,6 @@ config_data! { /// The warnings will be indicated by a blue squiggly underline in code /// and a blue icon in the `Problems Panel`. diagnostics_warningsAsInfo: Vec = "[]", - /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may /// also need to add the folders to Code's `files.watcherExclude`. @@ -895,6 +894,15 @@ impl Config { } } + pub fn add_linked_projects(&mut self, linked_projects: Vec) { + let mut linked_projects = linked_projects + .into_iter() + .map(ManifestOrProjectJson::ProjectJson) + .collect::>(); + + self.data.linkedProjects.append(&mut linked_projects); + } + pub fn did_save_text_document_dynamic_registration(&self) -> bool { let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?); caps.did_save == Some(true) && caps.dynamic_registration == Some(true) @@ -1129,16 +1137,16 @@ impl Config { pub fn cargo(&self) -> CargoConfig { let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| { if rustc_src == "discover" { - RustcSource::Discover + RustLibSource::Discover } else { - RustcSource::Path(self.root_path.join(rustc_src)) + RustLibSource::Path(self.root_path.join(rustc_src)) } }); let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| { if sysroot == "discover" { - RustcSource::Discover + RustLibSource::Discover } else { - RustcSource::Path(self.root_path.join(sysroot)) + RustLibSource::Path(self.root_path.join(sysroot)) } }); let sysroot_src = diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 715804449a045..313bb2ec8dffa 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -87,6 +87,42 @@ impl<'a> RequestDispatcher<'a> { self } + /// Dispatches the request onto thread pool + pub(crate) fn on_no_retry( + &mut self, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + R::Result: Serialize, + { + let (req, params, panic_context) = match self.parse::() { + Some(it) => it, + None => return self, + }; + + self.global_state.task_pool.handle.spawn({ + let world = self.global_state.snapshot(); + move || { + let result = panic::catch_unwind(move || { + let _pctx = stdx::panic_context::enter(panic_context); + f(world, params) + }); + match thread_result_to_response::(req.id.clone(), result) { + Ok(response) => Task::Response(response), + Err(_) => Task::Response(lsp_server::Response::new_err( + req.id, + lsp_server::ErrorCode::ContentModified as i32, + "content modified".to_string(), + )), + } + } + }); + + self + } + /// Dispatches the request onto thread pool pub(crate) fn on( &mut self, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 32ac9a42dec33..2fca2ab851d41 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -29,7 +29,7 @@ use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; -use vfs::AbsPathBuf; +use vfs::{AbsPath, AbsPathBuf}; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -46,6 +46,7 @@ use crate::{ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { state.proc_macro_clients.clear(); state.proc_macro_changed = false; + state.fetch_workspaces_queue.request_op("reload workspace request".to_string()); state.fetch_build_data_queue.request_op("reload workspace request".to_string()); Ok(()) @@ -84,6 +85,15 @@ pub(crate) fn handle_analyzer_status( snap.workspaces.len(), if snap.workspaces.len() == 1 { "" } else { "s" } ); + + format_to!( + buf, + "Workspace root folders: {:?}", + snap.workspaces + .iter() + .flat_map(|ws| ws.workspace_definition_path()) + .collect::>() + ); } buf.push_str("\nAnalysis:\n"); buf.push_str( diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 30f1c53c198f3..12e5caf2cc9e4 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -36,11 +36,41 @@ impl Progress { } impl GlobalState { - pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { - let message = message; - self.send_notification::( - lsp_types::ShowMessageParams { typ, message }, - ) + pub(crate) fn show_message( + &mut self, + typ: lsp_types::MessageType, + message: String, + show_open_log_button: bool, + ) { + match self.config.open_server_logs() && show_open_log_button { + true => self.send_request::( + lsp_types::ShowMessageRequestParams { + typ, + message, + actions: Some(vec![lsp_types::MessageActionItem { + title: "Open server logs".to_owned(), + properties: Default::default(), + }]), + }, + |this, resp| { + let lsp_server::Response { error: None, result: Some(result), .. } = resp + else { return }; + if let Ok(Some(_item)) = crate::from_json::< + ::Result, + >( + lsp_types::request::ShowMessageRequest::METHOD, &result + ) { + this.send_notification::(()); + } + }, + ), + false => self.send_notification::( + lsp_types::ShowMessageParams { + typ, + message, + }, + ), + } } /// Sends a notification to the client containing the error `message`. diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index dd0804b4398a9..67a54cde68c6f 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -406,9 +406,19 @@ impl GlobalState { 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); + } else if let (health, Some(message)) = (status.health, &status.message) { + let open_log_button = tracing::enabled!(tracing::Level::ERROR) + && (self.fetch_build_data_error().is_err() + || self.fetch_workspace_error().is_err()); + self.show_message( + match health { + lsp_ext::Health::Ok => lsp_types::MessageType::INFO, + lsp_ext::Health::Warning => lsp_types::MessageType::WARNING, + lsp_ext::Health::Error => lsp_types::MessageType::ERROR, + }, + message.clone(), + open_log_button, + ); } } } @@ -653,7 +663,7 @@ impl GlobalState { .on::(handlers::handle_goto_declaration) .on::(handlers::handle_goto_implementation) .on::(handlers::handle_goto_type_definition) - .on::(handlers::handle_inlay_hints) + .on_no_retry::(handlers::handle_inlay_hints) .on::(handlers::handle_inlay_hints_resolve) .on::(handlers::handle_completion) .on::(handlers::handle_completion_resolve) @@ -919,6 +929,7 @@ impl GlobalState { this.show_message( lsp_types::MessageType::WARNING, error.to_string(), + false, ); } this.update_configuration(config); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 28d37f5685ae9..1a6e1af2eb7ed 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -90,38 +90,55 @@ impl GlobalState { quiescent: self.is_quiescent(), message: None, }; + let mut message = String::new(); if self.proc_macro_changed { status.health = lsp_ext::Health::Warning; - status.message = - Some("Reload required due to source changes of a procedural macro.".into()) + message.push_str("Reload required due to source changes of a procedural macro.\n\n"); } if let Err(_) = self.fetch_build_data_error() { status.health = lsp_ext::Health::Warning; - status.message = - Some("Failed to run build scripts of some packages, check the logs.".to_string()); + message.push_str("Failed to run build scripts of some packages.\n\n"); } if !self.config.cargo_autoreload() && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() { status.health = lsp_ext::Health::Warning; - status.message = Some("Workspace reload required".to_string()) + message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n"); } - - if let Err(_) = self.fetch_workspace_error() { - status.health = lsp_ext::Health::Error; - status.message = Some("Failed to load workspaces".to_string()) - } - if self.config.linked_projects().is_empty() && self.config.detached_files().is_empty() && self.config.notifications().cargo_toml_not_found { status.health = lsp_ext::Health::Warning; - status.message = Some("Failed to discover workspace".to_string()) + message.push_str("Failed to discover workspace.\n\n"); + } + + for ws in self.workspaces.iter() { + let (ProjectWorkspace::Cargo { sysroot, .. } + | ProjectWorkspace::Json { sysroot, .. } + | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws; + if let Err(Some(e)) = sysroot { + status.health = lsp_ext::Health::Warning; + message.push_str(e); + message.push_str("\n\n"); + } + if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws { + status.health = lsp_ext::Health::Warning; + message.push_str(e); + message.push_str("\n\n"); + } } + if let Err(_) = self.fetch_workspace_error() { + status.health = lsp_ext::Health::Error; + message.push_str("Failed to load workspaces.\n\n"); + } + + if !message.is_empty() { + status.message = Some(message.trim_end().to_owned()); + } status } diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index db66d08a73b5e..c43d0830b9e24 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -48,23 +48,30 @@ impl From for ElseBranch { } impl ast::IfExpr { - pub fn then_branch(&self) -> Option { - self.children_after_condition().next() + pub fn condition(&self) -> Option { + // If the condition is a BlockExpr, check if the then body is missing. + // If it is assume the condition is the expression that is missing instead. + let mut exprs = support::children(self.syntax()); + let first = exprs.next(); + match first { + Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first), + first => first, + } } - pub fn else_branch(&self) -> Option { - let res = match self.children_after_condition().nth(1) { - Some(block) => ElseBranch::Block(block), - None => { - let elif = self.children_after_condition().next()?; - ElseBranch::IfExpr(elif) - } - }; - Some(res) + pub fn then_branch(&self) -> Option { + match support::children(self.syntax()).nth(1)? { + ast::Expr::BlockExpr(block) => Some(block), + _ => None, + } } - fn children_after_condition(&self) -> impl Iterator { - self.syntax().children().skip(1).filter_map(N::cast) + pub fn else_branch(&self) -> Option { + match support::children(self.syntax()).nth(2)? { + ast::Expr::BlockExpr(block) => Some(ElseBranch::Block(block)), + ast::Expr::IfExpr(elif) => Some(ElseBranch::IfExpr(elif)), + _ => None, + } } } @@ -356,7 +363,15 @@ impl ast::BlockExpr { Some(it) => it, None => return true, }; - !matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR) + match parent.kind() { + FOR_EXPR | IF_EXPR => parent + .children() + .filter(|it| ast::Expr::can_cast(it.kind())) + .next() + .map_or(true, |it| it == *self.syntax()), + LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false, + _ => true, + } } } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 15bd5ab3c7299..3308077da5b12 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -937,12 +937,6 @@ impl From for ast::Item { } } -impl ast::IfExpr { - pub fn condition(&self) -> Option { - support::child(&self.syntax) - } -} - impl ast::MatchGuard { pub fn condition(&self) -> Option { support::child(&self.syntax) diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 93ff76a040c6d..ca6de4061a4b8 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -44,6 +44,8 @@ //! try: infallible //! unsize: sized +#![rustc_coherence_is_core] + pub mod marker { // region:sized #[lang = "sized"] diff --git a/editors/code/package.json b/editors/code/package.json index a3b1a3107d0c9..c5eb08748bfab 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -199,6 +199,11 @@ "title": "Reload workspace", "category": "rust-analyzer" }, + { + "command": "rust-analyzer.addProject", + "title": "Add current file's crate to workspace", + "category": "rust-analyzer" + }, { "command": "rust-analyzer.reload", "title": "Restart server", @@ -428,6 +433,17 @@ "default": false, "type": "boolean" }, + "rust-analyzer.discoverProjectCommand": { + "markdownDescription": "Sets the command that rust-analyzer uses to generate `rust-project.json` files. This command should only be used\n if a build system like Buck or Bazel is also in use. The command must accept files as arguments and return \n a rust-project.json over stdout.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 62980ca046450..565cb9c6432f4 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -6,7 +6,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is"; import { assert } from "./util"; import * as diagnostics from "./diagnostics"; import { WorkspaceEdit } from "vscode"; -import { Config, substituteVSCodeVariables } from "./config"; +import { Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; export interface Env { @@ -95,7 +95,16 @@ export async function createClient( const resp = await next(params, token); if (resp && Array.isArray(resp)) { return resp.map((val) => { - return substituteVSCodeVariables(val); + return prepareVSCodeConfig(val, (key, cfg) => { + // we only want to set discovered workspaces on the right key + // and if a workspace has been discovered. + if ( + key === "linkedProjects" && + config.discoveredWorkspaces.length > 0 + ) { + cfg[key] = config.discoveredWorkspaces; + } + }); }); } else { return resp; diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index f4a4579a92c9b..8a953577e99d3 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient"; import * as ra from "./lsp_ext"; import * as path from "path"; -import { Ctx, Cmd, CtxInit } from "./ctx"; +import { Ctx, Cmd, CtxInit, discoverWorkspace } from "./ctx"; import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets"; import { spawnSync } from "child_process"; import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run"; @@ -749,6 +749,33 @@ export function reloadWorkspace(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.reloadWorkspace); } +export function addProject(ctx: CtxInit): Cmd { + return async () => { + const discoverProjectCommand = ctx.config.discoverProjectCommand; + if (!discoverProjectCommand) { + return; + } + + const workspaces: JsonProject[] = await Promise.all( + vscode.workspace.workspaceFolders!.map(async (folder): Promise => { + const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); + return discoverWorkspace(rustDocuments, discoverProjectCommand, { + cwd: folder.uri.fsPath, + }); + }) + ); + + ctx.addToDiscoveredWorkspaces(workspaces); + + // this is a workaround to avoid needing writing the `rust-project.json` into + // a workspace-level VS Code-specific settings folder. We'd like to keep the + // `rust-project.json` entirely in-memory. + await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", + }); + }; +} + async function showReferencesImpl( client: LanguageClient | undefined, uri: string, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1faa0ad91065b..da7c74c28bae9 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -34,6 +34,7 @@ export class Config { constructor(ctx: vscode.ExtensionContext) { this.globalStorageUri = ctx.globalStorageUri; + this.discoveredWorkspaces = []; vscode.workspace.onDidChangeConfiguration( this.onDidChangeConfiguration, this, @@ -55,6 +56,8 @@ export class Config { log.info("Using configuration", Object.fromEntries(cfg)); } + public discoveredWorkspaces: JsonProject[]; + private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { this.refreshLogging(); @@ -191,7 +194,7 @@ export class Config { * So this getter handles this quirk by not requiring the caller to use postfix `!` */ private get(path: string): T | undefined { - return substituteVSCodeVariables(this.cfg.get(path)); + return prepareVSCodeConfig(this.cfg.get(path)); } get serverPath() { @@ -214,6 +217,10 @@ export class Config { return this.get("trace.extension"); } + get discoverProjectCommand() { + return this.get("discoverProjectCommand"); + } + get cargoRunner() { return this.get("cargoRunner"); } @@ -280,18 +287,32 @@ export class Config { } } -export function substituteVSCodeVariables(resp: T): T { +// the optional `cb?` parameter is meant to be used to add additional +// key/value pairs to the VS Code configuration. This needed for, e.g., +// including a `rust-project.json` into the `linkedProjects` key as part +// of the configuration/InitializationParams _without_ causing VS Code +// configuration to be written out to workspace-level settings. This is +// undesirable behavior because rust-project.json files can be tens of +// thousands of lines of JSON, most of which is not meant for humans +// to interact with. +export function prepareVSCodeConfig( + resp: T, + cb?: (key: Extract, res: { [key: string]: any }) => void +): T { if (Is.string(resp)) { return substituteVSCodeVariableInString(resp) as T; } else if (resp && Is.array(resp)) { return resp.map((val) => { - return substituteVSCodeVariables(val); + return prepareVSCodeConfig(val); }) as T; } else if (resp && typeof resp === "object") { const res: { [key: string]: any } = {}; for (const key in resp) { const val = resp[key]; - res[key] = substituteVSCodeVariables(val); + res[key] = prepareVSCodeConfig(val); + if (cb) { + cb(key, res); + } } return res as T; } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 1708d47cee77d..c2dca733df8f5 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -2,12 +2,20 @@ import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; -import { Config, substituteVSCodeVariables } from "./config"; +import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; -import { isRustDocument, isRustEditor, LazyOutputChannel, log, RustEditor } from "./util"; +import { + executeDiscoverProject, + isRustDocument, + isRustEditor, + LazyOutputChannel, + log, + RustEditor, +} from "./util"; import { ServerStatusParams } from "./lsp_ext"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; +import { ExecOptions } from "child_process"; // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if // only those are in use. We use "Empty" to represent these scenarios @@ -41,6 +49,17 @@ export function fetchWorkspace(): Workspace { : { kind: "Workspace Folder" }; } +export async function discoverWorkspace( + files: readonly vscode.TextDocument[], + command: string[], + options: ExecOptions +): Promise { + const paths = files.map((f) => `"${f.uri.fsPath}"`).join(" "); + const joinedCommand = command.join(" "); + const data = await executeDiscoverProject(`${joinedCommand} ${paths}`, options); + return JSON.parse(data) as JsonProject; +} + export type CommandFactory = { enabled: (ctx: CtxInit) => Cmd; disabled?: (ctx: Ctx) => Cmd; @@ -52,7 +71,7 @@ export type CtxInit = Ctx & { export class Ctx { readonly statusBar: vscode.StatusBarItem; - readonly config: Config; + config: Config; readonly workspace: Workspace; private _client: lc.LanguageClient | undefined; @@ -169,7 +188,30 @@ export class Ctx { }; } - const initializationOptions = substituteVSCodeVariables(rawInitializationOptions); + const discoverProjectCommand = this.config.discoverProjectCommand; + if (discoverProjectCommand) { + const workspaces: JsonProject[] = await Promise.all( + vscode.workspace.workspaceFolders!.map(async (folder): Promise => { + const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); + return discoverWorkspace(rustDocuments, discoverProjectCommand, { + cwd: folder.uri.fsPath, + }); + }) + ); + + this.addToDiscoveredWorkspaces(workspaces); + } + + const initializationOptions = prepareVSCodeConfig( + rawInitializationOptions, + (key, obj) => { + // we only want to set discovered workspaces on the right key + // and if a workspace has been discovered. + if (key === "linkedProjects" && this.config.discoveredWorkspaces.length > 0) { + obj["linkedProjects"] = this.config.discoveredWorkspaces; + } + } + ); this._client = await createClient( this.traceOutputChannel, @@ -251,6 +293,17 @@ export class Ctx { return this._serverPath; } + addToDiscoveredWorkspaces(workspaces: JsonProject[]) { + for (const workspace of workspaces) { + const index = this.config.discoveredWorkspaces.indexOf(workspace); + if (~index) { + this.config.discoveredWorkspaces[index] = workspace; + } else { + this.config.discoveredWorkspaces.push(workspace); + } + } + } + private updateCommands(forceDisable?: "disable") { this.commandDisposables.forEach((disposable) => disposable.dispose()); this.commandDisposables = []; @@ -289,6 +342,7 @@ export class Ctx { statusBar.tooltip.appendText(status.message ?? "Ready"); statusBar.color = undefined; statusBar.backgroundColor = undefined; + statusBar.command = "rust-analyzer.stopServer"; break; case "warning": if (status.message) { @@ -298,6 +352,7 @@ export class Ctx { statusBar.backgroundColor = new vscode.ThemeColor( "statusBarItem.warningBackground" ); + statusBar.command = "rust-analyzer.openLogs"; icon = "$(warning) "; break; case "error": @@ -306,6 +361,7 @@ export class Ctx { } statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground"); statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground"); + statusBar.command = "rust-analyzer.openLogs"; icon = "$(error) "; break; case "stopped": @@ -315,18 +371,19 @@ export class Ctx { ); statusBar.color = undefined; statusBar.backgroundColor = undefined; + statusBar.command = "rust-analyzer.startServer"; statusBar.text = `$(stop-circle) rust-analyzer`; return; } if (statusBar.tooltip.value) { statusBar.tooltip.appendText("\n\n"); } - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)" ); - statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); + statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 400cd207d41b0..872d7199b838a 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -43,6 +43,7 @@ export const relatedTests = new lc.RequestType("rust-analyzer/reloadWorkspace"); + export const runFlycheck = new lc.NotificationType<{ textDocument: lc.TextDocumentIdentifier | null; }>("rust-analyzer/runFlycheck"); diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 8a2412af849cd..d5de00561b123 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -153,6 +153,7 @@ function createCommands(): Record { memoryUsage: { enabled: commands.memoryUsage }, shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, + addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, diff --git a/editors/code/src/rust_project.ts b/editors/code/src/rust_project.ts new file mode 100644 index 0000000000000..187a1a96c10c5 --- /dev/null +++ b/editors/code/src/rust_project.ts @@ -0,0 +1,91 @@ +interface JsonProject { + /// Path to the directory with *source code* of + /// sysroot crates. + /// + /// It should point to the directory where std, + /// core, and friends can be found: + /// + /// https://github.com/rust-lang/rust/tree/master/library. + /// + /// If provided, rust-analyzer automatically adds + /// dependencies on sysroot crates. Conversely, + /// if you omit this path, you can specify sysroot + /// dependencies yourself and, for example, have + /// several different "sysroots" in one graph of + /// crates. + sysroot_src?: string; + /// The set of crates comprising the current + /// project. Must include all transitive + /// dependencies as well as sysroot crate (libstd, + /// libcore and such). + crates: Crate[]; +} + +interface Crate { + /// Optional crate name used for display purposes, + /// without affecting semantics. See the `deps` + /// key for semantically-significant crate names. + display_name?: string; + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: "2015" | "2018" | "2021"; + /// Dependencies + deps: Dep[]; + /// Should this crate be treated as a member of + /// current "workspace". + /// + /// By default, inferred from the `root_module` + /// (members are the crates which reside inside + /// the directory opened in the editor). + /// + /// Set this to `false` for things like standard + /// library and 3rd party crates to enable + /// performance optimizations (rust-analyzer + /// assumes that non-member crates don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` + /// files comprising this crate. + /// + /// By default, rust-analyzer assumes that only + /// files under `root_module.parent` can belong + /// to a crate. `include_dirs` are included + /// recursively, unless a subdirectory is in + /// `exclude_dirs`. + /// + /// Different crates can share the same `source`. + /// + /// If two crates share an `.rs` file in common, + /// they *must* have the same `source`. + /// rust-analyzer assumes that files from one + /// source can't refer to files in another source. + source?: { + include_dirs: string[]; + exclude_dirs: string[]; + }; + /// The set of cfgs activated for a given crate, like + /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. + cfg: string[]; + /// Target triple for this Crate. + /// + /// Used when running `rustc --print cfg` + /// to get target-specific cfgs. + target?: string; + /// Environment variables, used for + /// the `env!` macro + env: { [key: string]: string }; + + /// Whether the crate is a proc-macro crate. + is_proc_macro: boolean; + /// For proc-macro crates, path to compiled + /// proc-macro (.so file). + proc_macro_dylib_path?: string; +} + +interface Dep { + /// Index of a crate in the `crates` array. + crate: number; + /// Name as should appear in the (implicit) + /// `extern crate name` declaration. + name: string; +} diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index d93b9caeb1648..922fbcbcf35a5 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -150,9 +150,11 @@ export function memoizeAsync( /** Awaitable wrapper around `child_process.exec` */ export function execute(command: string, options: ExecOptions): Promise { + log.info(`running command: ${command}`); return new Promise((resolve, reject) => { exec(command, options, (err, stdout, stderr) => { if (err) { + log.error(err); reject(err); return; } @@ -167,6 +169,21 @@ export function execute(command: string, options: ExecOptions): Promise }); } +export function executeDiscoverProject(command: string, options: ExecOptions): Promise { + log.info(`running command: ${command}`); + return new Promise((resolve, reject) => { + exec(command, options, (err, stdout, _) => { + if (err) { + log.error(err); + reject(err); + return; + } + + resolve(stdout.trimEnd()); + }); + }); +} + export class LazyOutputChannel implements vscode.OutputChannel { constructor(name: string) { this.name = name; From 3303a6eff5f759d5bc6f9a2e891bddab0d1f21e7 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Mon, 20 Mar 2023 21:48:01 +0330 Subject: [PATCH 019/806] Implement some intrinsics --- crates/hir-ty/src/consteval/tests.rs | 70 +++- .../hir-ty/src/consteval/tests/intrinsics.rs | 162 ++++++++ crates/hir-ty/src/infer/expr.rs | 11 +- crates/hir-ty/src/mir/eval.rs | 383 +++++++++++++----- crates/hir-ty/src/mir/lower.rs | 5 + crates/hir-ty/src/tests/simple.rs | 34 +- crates/ide/src/hover/render.rs | 1 - 7 files changed, 521 insertions(+), 145 deletions(-) create mode 100644 crates/hir-ty/src/consteval/tests/intrinsics.rs diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 2ba0cbd5db4f0..47ef26fc58606 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -11,6 +11,8 @@ use super::{ ConstEvalError, }; +mod intrinsics; + fn simplify(e: ConstEvalError) -> ConstEvalError { match e { ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => { @@ -82,6 +84,49 @@ fn bit_op() { check_number(r#"const GOAL: i8 = 1 << 8"#, 0); } +#[test] +fn casts() { + check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: i32 = { + let a = [10, 20, 3, 15]; + let x: &[i32] = &a; + let y: *const [i32] = x; + let z = y as *const i32; + unsafe { *z } + }; + "#, + 10, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: i16 = { + let a = &mut 5; + let z = a as *mut _; + unsafe { *z } + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = [10, 20, 3, 15]; + let x: &[i32] = &a; + let y: *const [i32] = x; + let z = y as *const [u8]; // slice fat pointer cast don't touch metadata + let w = unsafe { &*z }; + w.len() + }; + "#, + 4, + ); +} + #[test] fn locals() { check_number( @@ -279,20 +324,6 @@ fn function_call() { ); } -#[test] -fn intrinsics() { - check_number( - r#" - extern "rust-intrinsic" { - pub fn size_of() -> usize; - } - - const GOAL: usize = size_of::(); - "#, - 4, - ); -} - #[test] fn trait_basic() { check_number( @@ -1353,6 +1384,17 @@ fn array_and_index() { check_number( r#" //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = [1, 2, 3]; + let x: &[i32] = &a; + let y = &*x; + y.len() + };"#, + 3, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice const GOAL: usize = [1, 2, 3, 4, 5].len();"#, 5, ); diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs new file mode 100644 index 0000000000000..371d5cab3378d --- /dev/null +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -0,0 +1,162 @@ +use super::*; + +#[test] +fn size_of() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn size_of() -> usize; + } + + const GOAL: usize = size_of::(); + "#, + 4, + ); +} + +#[test] +fn transmute() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn transmute(e: T) -> U; + } + + const GOAL: i32 = transmute((1i16, 1i16)); + "#, + 0x00010001, + ); +} + +#[test] +fn const_eval_select() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn const_eval_select(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce, + F: FnOnce; + } + + const fn in_const(x: i32, y: i32) -> i32 { + x + y + } + + fn in_rt(x: i32, y: i32) -> i32 { + x + y + } + + const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt); + "#, + 5, + ); +} + +#[test] +fn wrapping_add() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn wrapping_add(a: T, b: T) -> T; + } + + const GOAL: u8 = wrapping_add(10, 250); + "#, + 4, + ); +} + +#[test] +fn offset() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn offset(dst: *const T, offset: isize) -> *const T; + } + + const GOAL: u8 = unsafe { + let ar: &[(u8, u8, u8)] = &[ + (10, 11, 12), + (20, 21, 22), + (30, 31, 32), + (40, 41, 42), + (50, 51, 52), + ]; + let ar: *const [(u8, u8, u8)] = ar; + let ar = ar as *const (u8, u8, u8); + let element = offset(ar, 2); + element.1 + }; + "#, + 31, + ); +} + +#[test] +fn arith_offset() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn arith_offset(dst: *const T, offset: isize) -> *const T; + } + + const GOAL: u8 = unsafe { + let ar: &[(u8, u8, u8)] = &[ + (10, 11, 12), + (20, 21, 22), + (30, 31, 32), + (40, 41, 42), + (50, 51, 52), + ]; + let ar: *const [(u8, u8, u8)] = ar; + let ar = ar as *const (u8, u8, u8); + let element = arith_offset(arith_offset(ar, 102), -100); + element.1 + }; + "#, + 31, + ); +} + +#[test] +fn copy_nonoverlapping() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + } + + const GOAL: u8 = unsafe { + let mut x = 2; + let y = 5; + copy_nonoverlapping(&y, &mut x, 1); + x + }; + "#, + 5, + ); +} + +#[test] +fn copy() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn copy(src: *const T, dst: *mut T, count: usize); + } + + const GOAL: i32 = unsafe { + let mut x = [1i32, 2, 3, 4, 5]; + let y = (&mut x as *mut _) as *mut i32; + let z = (y as usize + 4) as *const i32; + copy(z, y, 4); + x[0] + x[1] + x[2] + x[3] + x[4] + }; + "#, + 19, + ); +} diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 6d2aa59ea3598..d14b3f7140e4a 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -630,8 +630,15 @@ impl<'a> InferenceContext<'a> { Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); // FIXME: propagate the "castable to" expectation - let _inner_ty = self.infer_expr_no_expect(*expr); - // FIXME check the cast... + let inner_ty = self.infer_expr_no_expect(*expr); + match (inner_ty.kind(Interner), cast_ty.kind(Interner)) { + (TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => { + // FIXME: record invalid cast diagnostic in case of mismatch + self.unify(inner, cast); + } + // FIXME check the other kinds of cast... + _ => (), + } cast_ty } Expr::Ref { expr, rawness, mutability } => { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index f8545e88ad548..26bf877cf0b0f 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -96,11 +96,18 @@ enum Address { use Address::*; +#[derive(Debug, Clone, Copy)] struct Interval { addr: Address, size: usize, } +#[derive(Debug, Clone)] +struct IntervalAndTy { + interval: Interval, + ty: Ty, +} + impl Interval { fn new(addr: Address, size: usize) -> Self { Self { addr, size } @@ -110,11 +117,37 @@ impl Interval { memory.read_memory(self.addr, self.size) } + fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> { + memory.write_memory(self.addr, bytes) + } + + fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> { + // FIXME: this could be more efficent + let bytes = &interval.get(memory)?.to_vec(); + memory.write_memory(self.addr, bytes) + } + fn slice(self, range: Range) -> Interval { Interval { addr: self.addr.offset(range.start), size: range.len() } } } +impl IntervalAndTy { + fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + memory.read_memory(self.interval.addr, self.interval.size) + } + + fn new( + addr: Address, + ty: Ty, + evaluator: &Evaluator<'_>, + locals: &Locals<'_>, + ) -> Result { + let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; + Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) + } +} + enum IntervalOrOwned { Owned(Vec), Borrowed(Interval), @@ -135,7 +168,7 @@ impl Address { fn from_usize(x: usize) -> Self { if x > usize::MAX / 2 { - Stack(usize::MAX - x) + Stack(x - usize::MAX / 2) } else { Heap(x) } @@ -147,7 +180,7 @@ impl Address { fn to_usize(&self) -> usize { let as_num = match self { - Stack(x) => usize::MAX - *x, + Stack(x) => *x + usize::MAX / 2, Heap(x) => *x, }; as_num @@ -174,7 +207,7 @@ pub enum MirEvalError { /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. UndefinedBehavior(&'static str), - Panic, + Panic(String), MirLowerError(FunctionId, MirLowerError), TypeIsUnsized(Ty, &'static str), NotSupported(String), @@ -197,7 +230,7 @@ impl std::fmt::Debug for MirEvalError { Self::UndefinedBehavior(arg0) => { f.debug_tuple("UndefinedBehavior").field(arg0).finish() } - Self::Panic => write!(f, "Panic"), + Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"), Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"), Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."), Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"), @@ -289,7 +322,19 @@ impl Evaluator<'_> { } fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result

{ - Ok(self.place_addr_and_ty(p, locals)?.0) + Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) + } + + fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result { + let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; + Ok(Interval { + addr: place_addr_and_ty.0, + size: self.size_of_sized( + &place_addr_and_ty.1, + locals, + "Type of place that we need its interval", + )?, + }) } fn ptr_size(&self) -> usize { @@ -299,10 +344,15 @@ impl Evaluator<'_> { } } - fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> { + fn place_addr_and_ty_and_metadata<'a>( + &'a self, + p: &Place, + locals: &'a Locals<'a>, + ) -> Result<(Address, Ty, Option)> { 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)?; + let mut metadata = None; // locals are always sized for proj in &p.projection { match proj { ProjectionElem::Deref => { @@ -314,12 +364,18 @@ impl Evaluator<'_> { )) } }; + metadata = if self.size_of(&ty, locals)?.is_none() { + Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }) + } else { + None + }; 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())?); + metadata = None; // Result of index is always sized match &ty.data(Interner).kind { TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind { TyKind::Slice(inner) => { @@ -357,6 +413,7 @@ impl Evaluator<'_> { .clone(); let offset = layout.fields.offset(f).bytes_usize(); addr = addr.offset(offset); + metadata = None; // tuple field is always sized } _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")), }, @@ -386,6 +443,8 @@ impl Evaluator<'_> { .offset(u32::from(f.local_id.into_raw()) as usize) .bytes_usize(); addr = addr.offset(offset); + // FIXME: support structs with unsized fields + metadata = None; } _ => return Err(MirEvalError::TypeError("Only adt has fields")), }, @@ -396,7 +455,7 @@ impl Evaluator<'_> { ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), } } - Ok((addr, ty)) + Ok((addr, ty, metadata)) } fn layout(&self, ty: &Ty) -> Result { @@ -411,16 +470,23 @@ impl Evaluator<'_> { } fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result { - Ok(self.place_addr_and_ty(p, locals)?.1) + Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result { + fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result { Ok(match o { Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Constant(c) => c.data(Interner).ty.clone(), }) } + fn operand_ty_and_eval(&mut self, o: &Operand, locals: &Locals<'_>) -> Result { + Ok(IntervalAndTy { + interval: self.eval_operand(o, locals)?, + ty: self.operand_ty(o, locals)?, + }) + } + fn interpret_mir( &mut self, body: &MirBody, @@ -498,14 +564,19 @@ impl Evaluator<'_> { cleanup: _, from_hir_call: _, } => { + let destination = self.place_interval(destination, &locals)?; let fn_ty = self.operand_ty(func, &locals)?; + let args = args + .iter() + .map(|x| self.operand_ty_and_eval(x, &locals)) + .collect::>>()?; match &fn_ty.data(Interner).kind { TyKind::Function(_) => { let bytes = self.eval_operand(func, &locals)?; - self.exec_fn_pointer(bytes, destination, args, &locals)?; + self.exec_fn_pointer(bytes, destination, &args, &locals)?; } TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, args, &locals)?; + self.exec_fn_def(*def, generic_args, destination, &args, &locals)?; } x => not_supported!("unknown function type {x:?}"), } @@ -545,8 +616,12 @@ impl Evaluator<'_> { 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()) + let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; + let mut r = addr.to_bytes(); + if let Some(metadata) = metadata { + r.extend(metadata.get(self)?); + } + Owned(r) } Rvalue::Len(_) => not_supported!("rvalue len"), Rvalue::UnaryOp(op, val) => { @@ -624,8 +699,12 @@ impl Evaluator<'_> { 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::Div => l128.checked_div(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::Sub => l128.overflowing_sub(r128).0, BinOp::BitAnd => l128 & r128, BinOp::BitOr => l128 | r128, @@ -635,16 +714,16 @@ impl Evaluator<'_> { let r = r.to_le_bytes(); for &k in &r[lc.len()..] { if k != 0 && (k != 255 || !is_signed) { - return Err(MirEvalError::Panic); + return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); } } Owned(r[0..lc.len()].into()) } BinOp::Shl | BinOp::Shr => { let shift_amout = if r128 < 0 { - return Err(MirEvalError::Panic); + return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); } else if r128 > 128 { - return Err(MirEvalError::Panic); + return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); } else { r128 as u8 }; @@ -720,47 +799,54 @@ impl Evaluator<'_> { } 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); + Rvalue::Aggregate(kind, values) => { + let values = values + .iter() + .map(|x| self.eval_operand(x, locals)) + .collect::>>()?; + match kind { + AggregateKind::Array(_) => { + let mut r = vec![]; + for x in values { + let value = x.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.iter().copied(), + )?) + } + 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 = values[0].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 subst = self.subst_filler(subst, locals); + let (size, variant_layout, tag) = + self.layout_of_variant(*x, subst, locals)?; + Owned(self.make_by_layout( + size, + &variant_layout, + tag, + values.iter().copied(), + )?) } - 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 subst = self.subst_filler(subst, locals); - let (size, variant_layout, tag) = self.layout_of_variant(*x, subst, 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::ReifyFnPointer => { let current_ty = self.operand_ty(operand, locals)?; @@ -818,7 +904,9 @@ impl Evaluator<'_> { x => not_supported!("pointer cast {x:?}"), }, CastKind::DynStar => not_supported!("dyn star cast"), - CastKind::IntToInt => { + CastKind::IntToInt + | CastKind::PointerExposeAddress + | CastKind::PointerFromExposedAddress => { // FIXME: handle signed cast let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); let dest_size = @@ -828,7 +916,12 @@ impl Evaluator<'_> { 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::PtrToPtr => { + let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); + let dest_size = + self.size_of_sized(target_ty, locals, "destination of ptr to ptr cast")?; + Owned(current[0..dest_size].to_vec()) + } CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"), }, }) @@ -895,16 +988,15 @@ impl Evaluator<'_> { size: usize, // Not neccessarily equal to variant_layout.size variant_layout: &Layout, tag: Option<(usize, usize, i128)>, - values: &[Operand], - locals: &Locals<'_>, + values: impl Iterator, ) -> 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() { + for (i, op) in values.enumerate() { let offset = variant_layout.fields.offset(i).bytes_usize(); - let op = self.eval_operand(op, locals)?.get(&self)?; + let op = op.get(&self)?; result[offset..offset + op.len()].copy_from_slice(op); } Ok(result) @@ -1196,28 +1288,89 @@ impl Evaluator<'_> { } fn exec_intrinsic( - &self, + &mut self, as_str: &str, - mut arg_bytes: impl Iterator>, + args: &[IntervalAndTy], generic_args: Substitution, + destination: Interval, locals: &Locals<'_>, - ) -> Result> { + ) -> 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")), - } + let size = self.size_of_sized(ty, locals, "size_of arg")?; + destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) + } + "wrapping_add" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("const_eval_select args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_add(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "copy" | "copy_nonoverlapping" => { + let [src, dst, offset] = args else { + return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided")); + }; + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided")); + }; + let src = Address::from_bytes(src.get(self)?)?; + let dst = Address::from_bytes(dst.get(self)?)?; + let offset = from_bytes!(usize, offset.get(self)?); + let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; + let size = offset * size; + let src = Interval { addr: src, size }; + let dst = Interval { addr: dst, size }; + dst.write_from_interval(self, src) + } + "offset" | "arith_offset" => { + let [ptr, offset] = args else { + return Err(MirEvalError::TypeError("offset args are not provided")); + }; + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); + let offset = u128::from_le_bytes(pad16(offset.get(self)?, false)); + let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128; + let ans = ptr + offset * size; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => { + // FIXME: We should actually implement these checks + Ok(()) + } + "forget" => { + // We don't call any drop glue yet, so there is nothing here + Ok(()) } "transmute" => { - let Some(arg) = arg_bytes.next() else { + let [arg] = args else { return Err(MirEvalError::TypeError("trasmute arg is not provided")); }; - Ok(arg) + destination.write_from_interval(self, arg.interval) + } + "const_eval_select" => { + let [tuple, const_fn, _] = args else { + return Err(MirEvalError::TypeError("const_eval_select args are not provided")); + }; + let mut args = vec![const_fn.clone()]; + let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else { + return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple")); + }; + let layout = self.layout(&tuple.ty)?; + for (i, field) in fields.iter(Interner).enumerate() { + let field = field.assert_ty_ref(Interner).clone(); + let offset = layout.fields.offset(i).bytes_usize(); + let addr = tuple.interval.addr.offset(offset); + args.push(IntervalAndTy::new(addr, field, self, locals)?); + } + self.exec_fn_trait(&args, destination, locals) } _ => not_supported!("unknown intrinsic {as_str}"), } @@ -1226,8 +1379,8 @@ impl Evaluator<'_> { fn exec_fn_pointer( &mut self, bytes: Interval, - destination: &Place, - args: &[Operand], + destination: Interval, + args: &[IntervalAndTy], locals: &Locals<'_>, ) -> Result<()> { let id = from_bytes!(usize, bytes.get(self)?); @@ -1244,38 +1397,41 @@ impl Evaluator<'_> { &mut self, def: FnDefId, generic_args: &Substitution, - destination: &Place, - args: &[Operand], + destination: Interval, + args: &[IntervalAndTy], locals: &Locals<'_>, ) -> Result<()> { let def: CallableDefId = from_chalk(self.db, def); let generic_args = self.subst_filler(generic_args, &locals); match def { CallableDefId::FunctionId(def) => { - let dest_addr = self.place_addr(destination, &locals)?; - if let Some(x) = self.detect_fn_trait(def) { - self.exec_fn_trait(x, &args, destination, locals)?; + if let Some(_) = self.detect_fn_trait(def) { + self.exec_fn_trait(&args, destination, locals)?; return Ok(()); } - let arg_bytes = args - .iter() - .map(|x| Ok(self.eval_operand(x, &locals)?.get(&self)?.to_owned())) - .collect::>>()?; - self.exec_fn_with_args(def, arg_bytes, generic_args, locals, dest_addr)?; + self.exec_fn_with_args(def, args, generic_args, locals, destination)?; } 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)?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args.iter().map(|x| x.interval), + )?; + destination.write_from_bytes(self, &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)?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args.iter().map(|x| x.interval), + )?; + destination.write_from_bytes(self, &result)?; } } Ok(()) @@ -1284,10 +1440,10 @@ impl Evaluator<'_> { fn exec_fn_with_args( &mut self, def: FunctionId, - arg_bytes: Vec>, + args: &[IntervalAndTy], generic_args: Substitution, locals: &Locals<'_>, - dest_addr: Address, + destination: Interval, ) -> Result<()> { let function_data = self.db.function_data(def); let is_intrinsic = match &function_data.abi { @@ -1301,14 +1457,18 @@ impl Evaluator<'_> { _ => false, }, }; - let result = if is_intrinsic { - self.exec_intrinsic( + if is_intrinsic { + return self.exec_intrinsic( function_data.name.as_text().unwrap_or_default().as_str(), - arg_bytes.iter().cloned(), + args, generic_args, + destination, &locals, - )? - } else if let Some(x) = self.detect_lang_function(def) { + ); + } + let arg_bytes = + args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; + let result = if let Some(x) = self.detect_lang_function(def) { self.exec_lang_item(x, &arg_bytes)? } else { if let Some(self_ty_idx) = @@ -1321,9 +1481,12 @@ impl Evaluator<'_> { let ty = self .vtable_map .ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + let mut args_for_target = args.to_vec(); + args_for_target[0] = IntervalAndTy { + interval: args_for_target[0].interval.slice(0..self.ptr_size()), + ty: ty.clone(), + }; let ty = GenericArgData::Ty(ty.clone()).intern(Interner); - let mut args_for_target = arg_bytes; - args_for_target[0] = args_for_target[0][0..self.ptr_size()].to_vec(); let generics_for_target = Substitution::from_iter( Interner, generic_args.iter(Interner).enumerate().map(|(i, x)| { @@ -1336,10 +1499,10 @@ impl Evaluator<'_> { ); return self.exec_fn_with_args( def, - args_for_target, + &args_for_target, generics_for_target, locals, - dest_addr, + destination, ); } let (imp, generic_args) = @@ -1351,20 +1514,19 @@ impl Evaluator<'_> { self.interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args) .map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))? }; - self.write_memory(dest_addr, &result)?; + destination.write_from_bytes(self, &result)?; Ok(()) } fn exec_fn_trait( &mut self, - ft: FnTrait, - args: &[Operand], - destination: &Place, + args: &[IntervalAndTy], + destination: Interval, locals: &Locals<'_>, ) -> Result<()> { let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; - let mut func_ty = self.operand_ty(func, locals)?; - let mut func_data = self.eval_operand(func, locals)?; + let mut func_ty = func.ty.clone(); + let mut func_data = func.interval; while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) { func_ty = z.clone(); if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) { @@ -1383,7 +1545,7 @@ impl Evaluator<'_> { TyKind::Function(_) => { self.exec_fn_pointer(func_data, destination, &args[1..], locals)?; } - x => not_supported!("Call {ft:?} trait methods with type {x:?}"), + x => not_supported!("Call FnTrait methods with type {x:?}"), } Ok(()) } @@ -1392,7 +1554,10 @@ impl Evaluator<'_> { use LangItem::*; let mut args = args.iter(); match x { - PanicFmt | BeginPanic => Err(MirEvalError::Panic), + // FIXME: we want to find the panic message from arguments, but it wouldn't work + // currently even if we do that, since macro expansion of panic related macros + // is dummy. + PanicFmt | BeginPanic => Err(MirEvalError::Panic("".to_string())), SliceLen => { let arg = args .next() diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 5d9ae320726ff..65e3348b21896 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1285,6 +1285,11 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, (_, _) => CastKind::IntToInt, }, + (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, + (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, + (TyKind::Raw(..) | TyKind::Ref(..), TyKind::Raw(..) | TyKind::Ref(..)) => { + CastKind::PtrToPtr + } // Enum to int casts (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { CastKind::IntToInt diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 13cc3fea52d16..8322b9e1ca64a 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -2696,6 +2696,21 @@ fn f() { ) } +#[test] +fn infer_ref_to_raw_cast() { + check_types( + r#" +struct S; + +fn f() { + let s = &mut S; + let s = s as *mut _; + //^ *mut S +} + "#, + ); +} + #[test] fn infer_missing_type() { check_types( @@ -3258,25 +3273,6 @@ fn f(t: Ark) { ); } -// FIXME -#[test] -fn castable_to2() { - check_infer( - r#" -fn func() { - let x = &0u32 as *const _; -} -"#, - expect![[r#" - 10..44 '{ ...t _; }': () - 20..21 'x': *const {unknown} - 24..29 '&0u32': &u32 - 24..41 '&0u32 ...onst _': *const {unknown} - 25..29 '0u32': u32 - "#]], - ); -} - #[test] fn issue_14275() { // FIXME: evaluate const generic diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index da725ce502b8d..fb7b15e05d81f 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -409,7 +409,6 @@ pub(super) fn definition( } 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:?}")) From 8a3ad7c3d592adac72b1fce6bc208cdf7a40c8ad Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 21 Mar 2023 02:20:30 +0330 Subject: [PATCH 020/806] Support evaluating inherent associated constants with generics --- crates/hir-ty/src/consteval.rs | 10 ++++++---- crates/hir-ty/src/consteval/tests.rs | 8 ++++---- crates/hir-ty/src/db.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 15 --------------- crates/hir-ty/src/mir/eval.rs | 5 +++-- crates/hir-ty/src/mir/lower.rs | 11 ++++++----- crates/hir/src/lib.rs | 4 ++-- 7 files changed, 22 insertions(+), 33 deletions(-) diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index fcb3445a54270..7e69971fee789 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -15,7 +15,7 @@ use stdx::never; use crate::{ db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg, - Interner, MemoryMap, Ty, TyBuilder, + Interner, MemoryMap, Substitution, Ty, TyBuilder, }; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; @@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover( _: &dyn HirDatabase, _: &[String], _: &ConstId, + _: &Substitution, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } @@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover( pub(crate) fn const_eval_query( db: &dyn HirDatabase, const_id: ConstId, + subst: Substitution, ) -> Result { let def = const_id.into(); let body = db.mir_body(def)?; - let c = interpret_mir(db, &body, false)?; + let c = interpret_mir(db, &body, subst, false)?; Ok(c) } @@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant( return Ok(value); } let mir_body = db.mir_body(def)?; - let c = interpret_mir(db, &mir_body, false)?; + let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?; let c = try_const_usize(&c).unwrap() as i128; Ok(c) } @@ -234,7 +236,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, true) { + if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) { return result; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 47ef26fc58606..a0efc7541e3f5 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1,4 +1,5 @@ use base_db::fixture::WithFixture; +use chalk_ir::Substitution; use hir_def::db::DefDatabase; use crate::{ @@ -64,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result { _ => None, }) .unwrap(); - db.const_eval(const_id) + db.const_eval(const_id, Substitution::empty(Interner)) } #[test] @@ -1519,8 +1520,7 @@ fn const_generic_subst_fn() { #[test] fn const_generic_subst_assoc_const_impl() { - // FIXME: this should evaluate to 5 - check_fail( + check_number( r#" struct Adder; impl Adder { @@ -1528,7 +1528,7 @@ fn const_generic_subst_assoc_const_impl() { } const GOAL: usize = Adder::<2, 3>::VAL; "#, - ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")), + 5, ); } diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 8f1af4c2f8e2d..000944e0b5b0d 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] - fn const_eval(&self, def: ConstId) -> Result; + fn const_eval(&self, def: ConstId, subst: Substitution) -> Result; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d14b3f7140e4a..d52188bb284a2 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -612,21 +612,6 @@ impl<'a> InferenceContext<'a> { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) } - // Expr::Try { expr } => { - // let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); - // if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) { - // if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) { - // let subst = TyBuilder::subst_for_def(self.db, trait_, None) - // .push(inner_ty.clone()) - // .build(); - // self.write_method_resolution(tgt_expr, func, subst.clone()); - // } - // let try_output = self.resolve_output_on(trait_); - // self.resolve_associated_type(inner_ty, try_output) - // } else { - // self.err_ty() - // } - // } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); // FIXME: propagate the "castable to" expectation diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 26bf877cf0b0f..7b83645faef7d 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -282,6 +282,7 @@ struct Locals<'a> { pub fn interpret_mir( db: &dyn HirDatabase, body: &MirBody, + subst: Substitution, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -291,11 +292,11 @@ pub fn interpret_mir( ) -> Result { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); - let bytes = evaluator.interpret_mir_with_no_arg(&body)?; + let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?; let memory_map = evaluator.create_memory_map( &bytes, &ty, - &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) }, + &Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }, )?; return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 65e3348b21896..1821796be33c1 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -231,13 +231,13 @@ impl MirLowerCtx<'_> { let pr = match pr { ResolveValueResult::ValueNs(v) => v, ResolveValueResult::Partial(..) => { - if let Some(assoc) = self + if let Some((assoc, subst)) = self .infer .assoc_resolutions_for_expr(expr_id) { - match assoc.0 { + match assoc { hir_def::AssocItemId::ConstId(c) => { - self.lower_const(c, current, place, expr_id.into())?; + self.lower_const(c, current, place, subst, expr_id.into())?; return Ok(Some(current)) }, hir_def::AssocItemId::FunctionId(_) => { @@ -274,7 +274,7 @@ impl MirLowerCtx<'_> { Ok(Some(current)) } ValueNs::ConstId(const_id) => { - self.lower_const(const_id, current, place, expr_id.into())?; + self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?; Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { @@ -951,9 +951,10 @@ impl MirLowerCtx<'_> { const_id: hir_def::ConstId, prev_block: BasicBlockId, place: Place, + subst: Substitution, span: MirSpan, ) -> Result<()> { - let c = self.db.const_eval(const_id)?; + let c = self.db.const_eval(const_id, subst)?; self.write_const_to_place(c, prev_block, place, span) } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 35424feec8b29..dbf618afa6fed 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1801,7 +1801,7 @@ impl Function { let body = db .mir_body(self.id.into()) .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; - interpret_mir(db, &body, false)?; + interpret_mir(db, &body, Substitution::empty(Interner), false)?; Ok(()) } } @@ -1947,7 +1947,7 @@ impl Const { } pub fn render_eval(self, db: &dyn HirDatabase) -> Result { - let c = db.const_eval(self.id)?; + let c = db.const_eval(self.id, Substitution::empty(Interner))?; 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 From 9745a25d57fbd9a73aec1306554ac87936a63e7d Mon Sep 17 00:00:00 2001 From: x2cf Date: Tue, 21 Mar 2023 14:56:07 +0800 Subject: [PATCH 021/806] Fix VS Code status message formatting error --- editors/code/src/ctx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index c2dca733df8f5..85579453a63d7 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -383,7 +383,7 @@ export class Ctx { ); statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } From 28225cc33d60615aaa140dc4b0d32e2a1c21462c Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 18 Mar 2023 11:41:38 -0400 Subject: [PATCH 022/806] internal: Coalesce adjacent Indels --- crates/text-edit/src/lib.rs | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/text-edit/src/lib.rs b/crates/text-edit/src/lib.rs index 9bb4271b65f7f..4705d18187af8 100644 --- a/crates/text-edit/src/lib.rs +++ b/crates/text-edit/src/lib.rs @@ -176,6 +176,7 @@ impl TextEditBuilder { pub fn finish(self) -> TextEdit { let mut indels = self.indels; assert_disjoint_or_equal(&mut indels); + indels = coalesce_indels(indels); TextEdit { indels } } pub fn invalidates_offset(&self, offset: TextSize) -> bool { @@ -205,6 +206,21 @@ where indels.clone().zip(indels.skip(1)).all(|(l, r)| l.delete.end() <= r.delete.start() || l == r) } +fn coalesce_indels(indels: Vec) -> Vec { + indels + .into_iter() + .coalesce(|mut a, b| { + if a.delete.end() == b.delete.start() { + a.insert.push_str(&b.insert); + a.delete = TextRange::new(a.delete.start(), b.delete.end()); + Ok(a) + } else { + Err((a, b)) + } + }) + .collect_vec() +} + #[cfg(test)] mod tests { use super::{TextEdit, TextEditBuilder, TextRange}; @@ -261,4 +277,40 @@ mod tests { let edit2 = TextEdit::delete(range(9, 13)); assert!(edit1.union(edit2).is_err()); } + + #[test] + fn test_coalesce_disjoint() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "aa".into()); + builder.replace(range(5, 7), "bb".into()); + let edit = builder.finish(); + + assert_eq!(edit.indels.len(), 2); + } + + #[test] + fn test_coalesce_adjacent() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "aa".into()); + builder.replace(range(3, 5), "bb".into()); + + let edit = builder.finish(); + assert_eq!(edit.indels.len(), 1); + assert_eq!(edit.indels[0].insert, "aabb"); + assert_eq!(edit.indels[0].delete, range(1, 5)); + } + + #[test] + fn test_coalesce_adjacent_series() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "au".into()); + builder.replace(range(3, 5), "www".into()); + builder.replace(range(5, 8), "".into()); + builder.replace(range(8, 9), "ub".into()); + + let edit = builder.finish(); + assert_eq!(edit.indels.len(), 1); + assert_eq!(edit.indels[0].insert, "auwwwub"); + assert_eq!(edit.indels[0].delete, range(1, 9)); + } } From 91471c8da1594935389e360d1548a3cf511768f2 Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Thu, 23 Mar 2023 17:53:26 +0800 Subject: [PATCH 023/806] Rpath is not supported on AIX --- src/bootstrap/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 83a6d0ad29279..8c19f9557c8ef 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1554,7 +1554,7 @@ impl<'a> Builder<'a> { // flesh out rpath support more fully in the future. rustflags.arg("-Zosx-rpath-install-name"); Some("-Wl,-rpath,@loader_path/../lib") - } else if !target.contains("windows") { + } else if !target.contains("windows") && !target.contains("aix") { rustflags.arg("-Clink-args=-Wl,-z,origin"); Some("-Wl,-rpath,$ORIGIN/../lib") } else { From 3622fb645632fcf81af82919259be4b0012a3939 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Mar 2023 21:24:53 +0100 Subject: [PATCH 024/806] Fix lints --- editors/code/src/client.ts | 75 ++++++++++++++++++-------------------- editors/code/src/ctx.ts | 4 +- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2a1c757dfefd7..4ca6601a6aadb 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -121,59 +121,54 @@ export async function createClient( const preview = config.previewRustcOutput; const errorCode = config.useRustcErrorCode; diagnosticList.forEach((diag, idx) => { - let value = + const value = typeof diag.code === "string" || typeof diag.code === "number" ? diag.code : diag.code?.value; if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) { - let config = vscode.workspace.getConfiguration("rust-analyzer"); + const config = vscode.workspace.getConfiguration("rust-analyzer"); if (config.get("showUnlinkedFileNotification")) { unlinkedFiles.push(uri); - let folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; + const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; if (folder) { - let parent_backslash = uri.fsPath.lastIndexOf( + const parentBackslash = uri.fsPath.lastIndexOf( pathSeparator + "src" ); - let parent = uri.fsPath.substring(0, parent_backslash); + const parent = uri.fsPath.substring(0, parentBackslash); if (parent.startsWith(folder)) { - let path = vscode.Uri.file( + const path = vscode.Uri.file( parent + pathSeparator + "Cargo.toml" ); - void vscode.workspace.fs.stat(path).then(() => { - vscode.window - .showInformationMessage( - `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`, - "Yes", - "No", - "Don't show this again" - ) - .then((choice) => { - switch (choice) { - case "Yes": - break; - case "No": - config.update( - "linkedProjects", - config - .get("linkedProjects") - ?.concat( - path.fsPath.substring( - folder!.length - ) - ), - false - ); - break; - case "Don't show this again": - config.update( - "showUnlinkedFileNotification", - false, - false - ); - break; - } - }); + void vscode.workspace.fs.stat(path).then(async () => { + const choice = await vscode.window.showInformationMessage( + `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`, + "Yes", + "No", + "Don't show this again" + ); + switch (choice) { + case "Yes": + break; + case "No": + await config.update( + "linkedProjects", + config + .get("linkedProjects") + ?.concat( + path.fsPath.substring(folder.length) + ), + false + ); + break; + case "Don't show this again": + await config.update( + "showUnlinkedFileNotification", + false, + false + ); + break; + } }); } } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 5515921ed1492..8a0f4ea47732f 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -95,7 +95,6 @@ export class Ctx { ) { extCtx.subscriptions.push(this); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - this.statusBar.show(); this.workspace = workspace; this.clientSubscriptions = []; this.commandDisposables = []; @@ -338,6 +337,7 @@ export class Ctx { setServerStatus(status: ServerStatusParams | { health: "stopped" }) { let icon = ""; const statusBar = this.statusBar; + statusBar.show(); statusBar.tooltip = new vscode.MarkdownString("", true); statusBar.tooltip.isTrusted = true; switch (status.health) { @@ -386,7 +386,7 @@ export class Ctx { ); statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } From c01ba4a3101e343464093603b12bb5327d8a320f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 14:22:05 +0100 Subject: [PATCH 025/806] Reject symlinks in project-json --- crates/project-model/src/workspace.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index d1e53e12eebb5..2158485a3306f 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -4,7 +4,7 @@ use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc}; -use anyhow::{format_err, Context, Result}; +use anyhow::{bail, format_err, Context, Result}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult, @@ -154,6 +154,12 @@ impl ProjectWorkspace { ) -> Result { let res = match manifest { ProjectManifest::ProjectJson(project_json) => { + let metadata = fs::symlink_metadata(&project_json).with_context(|| { + format!("Failed to read json file {}", project_json.display()) + })?; + if metadata.is_symlink() { + bail!("The project-json may not currently point to a symlink"); + } let file = fs::read_to_string(&project_json).with_context(|| { format!("Failed to read json file {}", project_json.display()) })?; From 39e86e78c3e3a8fb35b93ac5e658b3379e1f9e6e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 14:43:08 +0100 Subject: [PATCH 026/806] Bump Cargo.lock --- Cargo.lock | 257 +++++++++++++++---------------- Cargo.toml | 4 + crates/flycheck/Cargo.toml | 4 +- crates/paths/Cargo.toml | 2 +- crates/proc-macro-api/Cargo.toml | 4 +- crates/profile/Cargo.toml | 2 +- crates/project-model/Cargo.toml | 4 +- crates/rust-analyzer/Cargo.toml | 14 +- crates/syntax/Cargo.toml | 2 +- lib/lsp-server/Cargo.toml | 4 +- 10 files changed, 150 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25242c6028a47..f97591411ee0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "anymap" @@ -40,9 +40,9 @@ checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" [[package]] name = "arbitrary" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0224938f92e7aef515fac2ff2d18bd1115c1394ddf4a092e0c87e8be9499ee5" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arrayvec" @@ -111,9 +111,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "camino" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform", @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg" @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "command-group" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "026c3922235f9f7d78f21251a026f3acdeb7cce3deba107fe09a4bfa63d850a2" +checksum = "5080df6b0f0ecb76cab30808f00d937ba725cebe266a3da8cd89dff92f2a9916" dependencies = [ "nix", "winapi", @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -267,9 +267,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -278,22 +278,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -313,9 +313,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf460bbff5f571bfc762da5102729f59f338be7db17a21fade44c5c4f5005350" +checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" dependencies = [ "proc-macro2", "quote", @@ -342,24 +342,24 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "ena" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" dependencies = [ "log", ] [[package]] name = "expect-test" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3" +checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" dependencies = [ "dissimilar", "once_cell", @@ -419,12 +419,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -442,9 +436,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "hashbrown" @@ -774,9 +768,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -832,9 +826,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jod-thread" @@ -874,9 +868,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -977,27 +971,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] @@ -1055,9 +1040,9 @@ dependencies = [ [[package]] name = "notify" -version = "5.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" dependencies = [ "bitflags", "crossbeam-channel", @@ -1068,7 +1053,7 @@ dependencies = [ "libc", "mio", "walkdir", - "winapi", + "windows-sys", ] [[package]] @@ -1093,18 +1078,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.2" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -1180,9 +1165,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "paths" @@ -1282,9 +1267,9 @@ version = "0.0.0" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] @@ -1366,18 +1351,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -1385,9 +1370,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1406,9 +1391,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "regex-syntax", ] @@ -1424,19 +1409,19 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rowan" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332" +checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf" dependencies = [ "countme", "hashbrown", - "memoffset 0.6.5", + "memoffset", "rustc-hash", "text-size", ] @@ -1451,6 +1436,7 @@ dependencies = [ "crossbeam-channel", "dissimilar", "expect-test", + "filetime", "flycheck", "hir", "hir-def", @@ -1464,9 +1450,11 @@ dependencies = [ "lsp-types", "mbe", "mimalloc", + "mio", "num_cpus", "oorandom", "parking_lot 0.12.1", + "parking_lot_core 0.9.6", "proc-macro-api", "proc-macro-srv", "profile", @@ -1476,10 +1464,12 @@ dependencies = [ "scip", "serde", "serde_json", + "serde_repr", "sourcegen", "stdx", "syntax", "test-utils", + "thiserror", "threadpool", "tikv-jemallocator", "toolchain", @@ -1506,9 +1496,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -1518,9 +1508,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "salsa" @@ -1583,27 +1573,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" dependencies = [ "proc-macro2", "quote", @@ -1612,9 +1602,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "indexmap", "itoa", @@ -1624,9 +1614,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" dependencies = [ "proc-macro2", "quote", @@ -1650,9 +1640,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smol_str" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44" +checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" dependencies = [ "serde", ] @@ -1689,9 +1679,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1763,18 +1753,18 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", @@ -1783,10 +1773,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -1812,12 +1803,11 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -1833,9 +1823,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "serde", "time-core", @@ -1858,9 +1848,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toolchain" @@ -1973,15 +1963,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1994,9 +1984,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-xid" @@ -2054,12 +2044,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -2117,45 +2106,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "write-json" diff --git a/Cargo.toml b/Cargo.toml index 333f03ce2ffe5..511cb24a61efb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,5 +74,9 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } + # non-local crates smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] } +# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved +serde = { version = "=1.0.156", features = ["derive"] } +serde_json = "1.0.94" diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 609d18c4eea33..1e0b3605b1659 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -16,8 +16,8 @@ crossbeam-channel = "0.5.5" tracing = "0.1.37" cargo_metadata = "0.15.0" rustc-hash = "1.1.0" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = "1.0.86" +serde_json.workspace = true +serde.workspace = true jod-thread = "0.1.2" command-group = "2.0.1" diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml index e24e6eceffbd9..28b54be5212f4 100644 --- a/crates/paths/Cargo.toml +++ b/crates/paths/Cargo.toml @@ -15,4 +15,4 @@ doctest = false # Adding this dep sadly puts a lot of rust-analyzer crates after the # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! -# serde = "1" +# serde.workspace = true diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 28469b832468b..e748421f6dd9b 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -19,8 +19,8 @@ object = { version = "0.30.2", default-features = false, features = [ "macho", "pe", ] } -serde = { version = "1.0.137", features = ["derive"] } -serde_json = { version = "1.0.81", features = ["unbounded_depth"] } +serde.workspace = true +serde_json = { workspace = true, features = ["unbounded_depth"] } tracing = "0.1.37" memmap2 = "0.5.4" snap = "1.1.0" diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 6273ea51db839..602e742751063 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -20,7 +20,7 @@ countme = { version = "3.0.1", features = ["enable"] } jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(target_os = "linux")'.dependencies] -perf-event = "0.4.7" +perf-event = "=0.4.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] } diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 22d6a6e78957c..080b224e86271 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -16,8 +16,8 @@ tracing = "0.1.35" rustc-hash = "1.1.0" cargo_metadata = "0.15.0" semver = "1.0.14" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = "1.0.86" +serde_json.workspace = true +serde.workspace = true anyhow = "1.0.62" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index f0f1900c78c56..1a4155d3bbb3c 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -29,8 +29,8 @@ parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" rustc-hash = "1.1.0" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = { version = "1.0.81", features = ["preserve_order"] } +serde_json = { workspace = true, features = ["preserve_order"] } +serde.workspace = true threadpool = "1.8.1" rayon = "1.6.1" num_cpus = "1.15.0" @@ -47,6 +47,16 @@ tracing-log = "0.1.3" tracing-tree = "0.2.1" always-assert = "0.1.2" +# These dependencies are unused, but we pin them to a version here to restrict them for our transitive dependencies +# so that we don't pull in duplicates of their depdendenceies like windows-sys and syn 1 vs 2 +# these would pull in serde 2 +thiserror = "=1.0.39" +serde_repr = "=0.1.11" +# these would pull in windows-sys 0.45.0 +mio = "=0.8.5" +filetime = "=0.2.19" +parking_lot_core = "=0.9.6" + cfg.workspace = true flycheck.workspace = true hir-def.workspace = true diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 305cf2d394b44..7200df0a5736a 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -16,7 +16,7 @@ doctest = false cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" -rowan = "0.15.10" +rowan = "0.15.11" rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } rustc-hash = "1.1.0" once_cell = "1.17.0" diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 6e32e3960526f..e78a9d2eb1674 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] log = "0.4.17" -serde_json = "1.0.86" -serde = { version = "1.0.144", features = ["derive"] } +serde_json.workspace = true +serde.workspace = true crossbeam-channel = "0.5.6" [dev-dependencies] From 3ae9bfe2661c4a3542a5b44cb1f9700cb17d8f30 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 15:43:58 +0100 Subject: [PATCH 027/806] Remove client side proc-macro version check --- crates/proc-macro-api/src/lib.rs | 14 ++------------ crates/rust-analyzer/src/reload.rs | 3 +-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 90d06967e8fc0..4525d9dfdd72f 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -54,18 +54,8 @@ pub struct MacroDylib { } impl MacroDylib { - // FIXME: this is buggy due to TOCTOU, we should check the version in the - // macro process instead. - pub fn new(path: AbsPathBuf) -> io::Result { - let _p = profile::span("MacroDylib::new"); - - let info = version::read_dylib_info(&path)?; - if info.version.0 < 1 || info.version.1 < 47 { - let msg = format!("proc-macro {} built by {info:#?} is not supported by rust-analyzer, please update your Rust version.", path.display()); - return Err(io::Error::new(io::ErrorKind::InvalidData, msg)); - } - - Ok(MacroDylib { path }) + pub fn new(path: AbsPathBuf) -> MacroDylib { + MacroDylib { path } } } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 1a6e1af2eb7ed..138b8446b9082 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -648,8 +648,7 @@ pub(crate) fn load_proc_macro( ) -> ProcMacroLoadResult { let server = server.map_err(ToOwned::to_owned)?; let res: Result, String> = (|| { - let dylib = MacroDylib::new(path.to_path_buf()) - .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?; + let dylib = MacroDylib::new(path.to_path_buf()); let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; if vec.is_empty() { return Err("proc macro library returned no proc macros".to_string()); From d154ea88f90e570b9d88dba021050f9030238c79 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 16:42:52 +0100 Subject: [PATCH 028/806] Split out proc-macros from the CrateGraph --- crates/base-db/src/change.rs | 10 ++- crates/base-db/src/fixture.rs | 17 ++-- crates/base-db/src/input.rs | 24 +++-- crates/base-db/src/lib.rs | 6 +- crates/hir-def/src/nameres/collector.rs | 44 +++++---- crates/hir-expand/src/proc_macro.rs | 9 +- crates/ide/src/lib.rs | 1 - crates/ide/src/shuffle_crate_graph.rs | 7 +- crates/project-model/src/tests.rs | 100 +++------------------ crates/project-model/src/workspace.rs | 66 ++++++++------ crates/rust-analyzer/src/cli/load_cargo.rs | 18 +++- crates/rust-analyzer/src/reload.rs | 13 +-- 12 files changed, 140 insertions(+), 175 deletions(-) diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index b57f2345767d0..b906511dbcfe2 100644 --- a/crates/base-db/src/change.rs +++ b/crates/base-db/src/change.rs @@ -6,7 +6,7 @@ use std::{fmt, sync::Arc}; use salsa::Durability; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; +use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] @@ -14,6 +14,7 @@ pub struct Change { pub roots: Option>, pub files_changed: Vec<(FileId, Option>)>, pub crate_graph: Option, + pub proc_macros: Option, } impl fmt::Debug for Change { @@ -49,6 +50,10 @@ impl Change { self.crate_graph = Some(graph); } + pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { + self.proc_macros = Some(proc_macros); + } + pub fn apply(self, db: &mut dyn SourceDatabaseExt) { let _p = profile::span("RootDatabase::apply_change"); if let Some(roots) = self.roots { @@ -73,6 +78,9 @@ impl Change { if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) } + if let Some(proc_macros) = self.proc_macros { + db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH) + } } } diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 8a7e9dfadfed2..7269180a5d611 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -12,8 +12,8 @@ use vfs::{file_set::FileSet, VfsPath}; use crate::{ input::{CrateName, CrateOrigin, LangCrateOrigin}, Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, - FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt, - SourceRoot, SourceRootId, + FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, + SourceDatabaseExt, SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -100,7 +100,7 @@ impl ChangeFixture { pub fn parse_with_proc_macros( ra_fixture: &str, - mut proc_macros: Vec<(String, ProcMacro)>, + mut proc_macro_defs: Vec<(String, ProcMacro)>, ) -> ChangeFixture { let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture); let mut change = Change::new(); @@ -160,7 +160,6 @@ impl ChangeFixture { meta.cfg.clone(), meta.cfg, meta.env, - Ok(Vec::new()), false, origin, meta.target_data_layout @@ -200,7 +199,6 @@ impl ChangeFixture { default_cfg.clone(), default_cfg, Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, default_target_data_layout @@ -244,7 +242,6 @@ impl ChangeFixture { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::Lang(LangCrateOrigin::Core), target_layout.clone(), @@ -257,12 +254,13 @@ impl ChangeFixture { } } + let mut proc_macros = ProcMacros::default(); if !proc_macro_names.is_empty() { let proc_lib_file = file_id; file_id.0 += 1; - proc_macros.extend(default_test_proc_macros()); - let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros); + proc_macro_defs.extend(default_test_proc_macros()); + let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs); let mut fs = FileSet::default(); fs.insert( proc_lib_file, @@ -282,11 +280,11 @@ impl ChangeFixture { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(proc_macro), true, CrateOrigin::CratesIo { repo: None, name: None }, target_layout, ); + proc_macros.insert(proc_macros_crate, Ok(proc_macro)); for krate in all_crates { crate_graph @@ -305,6 +303,7 @@ impl ChangeFixture { roots.push(root); change.set_roots(roots); change.set_crate_graph(crate_graph); + change.set_proc_macros(proc_macros); ChangeFixture { file_position, files, change } } diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 43388e915b5d3..41a2abd803a62 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -15,6 +15,8 @@ use syntax::SmolStr; use tt::token_id::Subtree; use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath}; +pub type ProcMacros = FxHashMap; + /// Files are grouped into source roots. A source root is a directory on the /// file systems which is watched for changes. Typically it corresponds to a /// Rust crate. Source roots *might* be nested: in this case, a file belongs to @@ -269,7 +271,6 @@ pub struct CrateData { pub target_layout: TargetLayoutLoadResult, pub env: Env, pub dependencies: Vec, - pub proc_macro: ProcMacroLoadResult, pub origin: CrateOrigin, pub is_proc_macro: bool, } @@ -322,7 +323,6 @@ impl CrateGraph { cfg_options: CfgOptions, potential_cfg_options: CfgOptions, env: Env, - proc_macro: ProcMacroLoadResult, is_proc_macro: bool, origin: CrateOrigin, target_layout: Result, Arc>, @@ -335,7 +335,6 @@ impl CrateGraph { cfg_options, potential_cfg_options, env, - proc_macro, dependencies: Vec::new(), origin, target_layout, @@ -460,7 +459,12 @@ impl CrateGraph { /// /// The ids of the crates in the `other` graph are shifted by the return /// amount. - pub fn extend(&mut self, other: CrateGraph) -> u32 { + pub fn extend( + &mut self, + other: CrateGraph, + proc_macros: &mut ProcMacros, + other_proc_macros: ProcMacros, + ) -> u32 { let start = self.arena.len() as u32; self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { let new_id = id.shift(start); @@ -469,6 +473,8 @@ impl CrateGraph { } (new_id, data) })); + proc_macros + .extend(other_proc_macros.into_iter().map(|(id, macros)| (id.shift(start), macros))); start } @@ -645,7 +651,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -658,7 +663,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -671,7 +675,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -698,7 +701,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -711,7 +713,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -735,7 +736,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -748,7 +748,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -761,7 +760,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -785,7 +783,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), @@ -798,7 +795,6 @@ mod tests { CfgOptions::default(), CfgOptions::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("".into()), diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 9720db9d8ace3..7ab9aa8709c04 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -16,7 +16,7 @@ pub use crate::{ input::{ CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId, + ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacros, SourceRoot, SourceRootId, TargetLayoutLoadResult, }, }; @@ -73,6 +73,10 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// The crate graph. #[salsa::input] fn crate_graph(&self) -> Arc; + + /// The crate graph. + #[salsa::input] + fn proc_macros(&self) -> Arc; } fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse { diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index ddcee77ec4ccf..b3ce913d9ab57 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -78,25 +78,35 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T } let cfg_options = &krate.cfg_options; - let proc_macros = match &krate.proc_macro { - Ok(proc_macros) => { - proc_macros - .iter() - .enumerate() - .map(|(idx, it)| { - // FIXME: a hacky way to create a Name from string. - let name = - tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; - (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) - }) - .collect() - } - Err(e) => { - def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str()); - Vec::new() + + let is_proc_macro = krate.is_proc_macro; + let proc_macros = if is_proc_macro { + match db.proc_macros().get(&def_map.krate) { + Some(Ok(proc_macros)) => { + proc_macros + .iter() + .enumerate() + .map(|(idx, it)| { + // FIXME: a hacky way to create a Name from string. + let name = + tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; + (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) + }) + .collect() + } + Some(Err(e)) => { + def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str()); + Vec::new() + } + None => { + def_map.proc_macro_loading_error = + Some("No proc-macros present for crate".to_owned().into_boxed_str()); + Vec::new() + } } + } else { + vec![] }; - let is_proc_macro = krate.is_proc_macro; let mut collector = DefCollector { db, diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index d758e9302cd87..ad9893587406a 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -33,10 +33,10 @@ impl ProcMacroExpander { ) -> ExpandResult { match self.proc_macro_id { Some(id) => { - let krate_graph = db.crate_graph(); - let proc_macros = match &krate_graph[def_crate].proc_macro { - Ok(proc_macros) => proc_macros, - Err(_) => { + let proc_macros = db.proc_macros(); + let proc_macros = match proc_macros.get(&def_crate) { + Some(Ok(proc_macros)) => proc_macros, + Some(Err(_)) | None => { never!("Non-dummy expander even though there are no proc macros"); return ExpandResult::with_err( tt::Subtree::empty(), @@ -59,6 +59,7 @@ impl ProcMacroExpander { } }; + let krate_graph = db.crate_graph(); // Proc macros have access to the environment variables of the invoking crate. let env = &krate_graph[calling_crate].env; match proc_macro.expander.expand(tt, attr_arg, env) { diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 078b66dd3955f..8477a8e6228d5 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -235,7 +235,6 @@ impl Analysis { cfg_options.clone(), cfg_options, Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, Err("Analysis::from_single_file has no target layout".into()), diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index e606072a82375..471c36dfecf57 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, SourceDatabase}, + base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase}, FxHashMap, RootDatabase, }; @@ -16,6 +16,7 @@ use ide_db::{ // |=== pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { let crate_graph = db.crate_graph(); + let proc_macros = db.proc_macros(); let mut shuffled_ids = crate_graph.iter().collect::>(); @@ -23,6 +24,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize); let mut new_graph = CrateGraph::default(); + let mut new_proc_macros = ProcMacros::default(); let mut map = FxHashMap::default(); for old_id in shuffled_ids.iter().copied() { @@ -35,11 +37,11 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.cfg_options.clone(), data.potential_cfg_options.clone(), data.env.clone(), - data.proc_macro.clone(), data.is_proc_macro, data.origin.clone(), data.target_layout.clone(), ); + new_proc_macros.insert(new_id, proc_macros[&old_id].clone()); map.insert(old_id, new_id); } @@ -53,4 +55,5 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { } db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH); + db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH); } diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 3754accbb03d8..ed78d71a1a57a 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use base_db::{CrateGraph, FileId}; +use base_db::{CrateGraph, FileId, ProcMacros}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect, Expect}; use paths::{AbsPath, AbsPathBuf}; @@ -14,11 +14,11 @@ use crate::{ WorkspaceBuildScripts, }; -fn load_cargo(file: &str) -> CrateGraph { +fn load_cargo(file: &str) -> (CrateGraph, ProcMacros) { load_cargo_with_overrides(file, CfgOverrides::default()) } -fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGraph { +fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> (CrateGraph, ProcMacros) { let meta = get_test_json_file(file); let cargo_workspace = CargoWorkspace::new(meta); let project_workspace = ProjectWorkspace::Cargo { @@ -34,7 +34,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr to_crate_graph(project_workspace) } -fn load_rust_project(file: &str) -> CrateGraph { +fn load_rust_project(file: &str) -> (CrateGraph, ProcMacros) { let data = get_test_json_file(file); let project = rooted_project_json(data); let sysroot = Ok(get_fake_sysroot()); @@ -92,7 +92,7 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { ProjectJson::new(base, data) } -fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph { +fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacros) { project_workspace.to_crate_graph( &mut |_, _| Ok(Vec::new()), &mut { @@ -117,7 +117,8 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { let cfg_overrides = CfgOverrides::Wildcard( CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), ); - let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); + let (crate_graph, _proc_macros) = + load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( crate_graph, expect![[r#" @@ -184,9 +185,6 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -265,9 +263,6 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -346,9 +341,6 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -427,9 +419,6 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -498,9 +487,6 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, }, dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: Some( "https://github.com/rust-lang/libc", @@ -527,7 +513,8 @@ fn cargo_hello_world_project_model_with_selective_overrides() { .collect(), ) }; - let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); + let (crate_graph, _proc_macros) = + load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( crate_graph, expect![[r#" @@ -596,9 +583,6 @@ fn cargo_hello_world_project_model_with_selective_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -679,9 +663,6 @@ fn cargo_hello_world_project_model_with_selective_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -762,9 +743,6 @@ fn cargo_hello_world_project_model_with_selective_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -845,9 +823,6 @@ fn cargo_hello_world_project_model_with_selective_overrides() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -916,9 +891,6 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, }, dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: Some( "https://github.com/rust-lang/libc", @@ -936,7 +908,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { #[test] fn cargo_hello_world_project_model() { - let crate_graph = load_cargo("hello-world-metadata.json"); + let (crate_graph, _proc_macros) = load_cargo("hello-world-metadata.json"); check_crate_graph( crate_graph, expect![[r#" @@ -1005,9 +977,6 @@ fn cargo_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -1088,9 +1057,6 @@ fn cargo_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -1171,9 +1137,6 @@ fn cargo_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -1254,9 +1217,6 @@ fn cargo_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: None, name: Some( @@ -1325,9 +1285,6 @@ fn cargo_hello_world_project_model() { }, }, dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), origin: CratesIo { repo: Some( "https://github.com/rust-lang/libc", @@ -1345,7 +1302,7 @@ fn cargo_hello_world_project_model() { #[test] fn rust_project_hello_world_project_model() { - let crate_graph = load_rust_project("hello-world-project.json"); + let (crate_graph, _proc_macros) = load_rust_project("hello-world-project.json"); check_crate_graph( crate_graph, expect![[r#" @@ -1390,9 +1347,6 @@ fn rust_project_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Alloc, ), @@ -1427,9 +1381,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Core, ), @@ -1464,9 +1415,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1501,9 +1449,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1557,9 +1502,6 @@ fn rust_project_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1594,9 +1536,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1704,9 +1643,6 @@ fn rust_project_hello_world_project_model() { prelude: true, }, ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Std, ), @@ -1741,9 +1677,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1778,9 +1711,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Test, ), @@ -1815,9 +1745,6 @@ fn rust_project_hello_world_project_model() { entries: {}, }, dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), origin: Lang( Other, ), @@ -1889,9 +1816,6 @@ fn rust_project_hello_world_project_model() { prelude: false, }, ], - proc_macro: Err( - "no proc macro dylib present", - ), origin: CratesIo { repo: None, name: Some( @@ -1907,7 +1831,7 @@ fn rust_project_hello_world_project_model() { #[test] fn rust_project_is_proc_macro_has_proc_macro_dep() { - let crate_graph = load_rust_project("is-proc-macro-project.json"); + let (crate_graph, _proc_macros) = load_rust_project("is-proc-macro-project.json"); // Since the project only defines one crate (outside the sysroot crates), // it should be the one with the biggest Id. let crate_id = crate_graph.iter().max().unwrap(); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 2158485a3306f..5766859143b20 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc}; use anyhow::{bail, format_err, Context, Result}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult, + FileId, LangCrateOrigin, ProcMacroLoadResult, ProcMacros, TargetLayoutLoadResult, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; @@ -579,10 +579,10 @@ impl ProjectWorkspace { load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, extra_env: &FxHashMap, - ) -> CrateGraph { + ) -> (CrateGraph, ProcMacros) { let _p = profile::span("ProjectWorkspace::to_crate_graph"); - let mut crate_graph = match self { + let (mut crate_graph, proc_macros) = match self { ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph( rustc_cfg.clone(), load_proc_macro, @@ -630,7 +630,7 @@ impl ProjectWorkspace { } else { tracing::debug!("Did not patch std to depend on cfg-if") } - crate_graph + (crate_graph, proc_macros) } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { @@ -685,8 +685,9 @@ fn project_json_to_crate_graph( sysroot: Option<&Sysroot>, extra_env: &FxHashMap, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { +) -> (CrateGraph, ProcMacros) { let mut crate_graph = CrateGraph::default(); + let mut proc_macros = FxHashMap::<_, _>::default(); let sysroot_deps = sysroot.as_ref().map(|sysroot| { sysroot_to_crate_graph( &mut crate_graph, @@ -707,13 +708,15 @@ fn project_json_to_crate_graph( }) .map(|(crate_id, krate, file_id)| { let env = krate.env.clone().into_iter().collect(); - let proc_macro = match krate.proc_macro_dylib_path.clone() { - Some(it) => load_proc_macro( - krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""), - &it, - ), - None => Err("no proc macro dylib present".into()), - }; + if let Some(it) = krate.proc_macro_dylib_path.clone() { + proc_macros.insert( + crate_id, + load_proc_macro( + krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""), + &it, + ), + ); + } let target_cfgs = match krate.target.as_deref() { Some(target) => cfg_cache @@ -734,7 +737,6 @@ fn project_json_to_crate_graph( cfg_options.clone(), cfg_options, env, - proc_macro, krate.is_proc_macro, if krate.display_name.is_some() { CrateOrigin::CratesIo { @@ -776,7 +778,7 @@ fn project_json_to_crate_graph( } } } - crate_graph + (crate_graph, proc_macros) } fn cargo_to_crate_graph( @@ -789,9 +791,10 @@ fn cargo_to_crate_graph( override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { +) -> (CrateGraph, ProcMacros) { let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); + let mut proc_macros = FxHashMap::default(); let (public_deps, libproc_macro) = match sysroot { Some(sysroot) => sysroot_to_crate_graph( &mut crate_graph, @@ -855,6 +858,7 @@ fn cargo_to_crate_graph( if let Some(file_id) = load(&cargo[tgt].root) { let crate_id = add_target_crate_root( &mut crate_graph, + &mut proc_macros, &cargo[pkg], build_scripts.get_output(pkg), cfg_options.clone(), @@ -931,6 +935,7 @@ fn cargo_to_crate_graph( if let Some((rustc_workspace, rustc_build_scripts)) = rustc { handle_rustc_crates( &mut crate_graph, + &mut proc_macros, &mut pkg_to_lib_crate, load, load_proc_macro, @@ -952,7 +957,7 @@ fn cargo_to_crate_graph( ); } } - crate_graph + (crate_graph, proc_macros) } fn detached_files_to_crate_graph( @@ -961,7 +966,7 @@ fn detached_files_to_crate_graph( detached_files: &[AbsPathBuf], sysroot: Option<&Sysroot>, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { +) -> (CrateGraph, ProcMacros) { let _p = profile::span("detached_files_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, _libproc_macro) = match sysroot { @@ -998,7 +1003,6 @@ fn detached_files_to_crate_graph( cfg_options.clone(), cfg_options.clone(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, @@ -1009,11 +1013,12 @@ fn detached_files_to_crate_graph( public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); } - crate_graph + (crate_graph, FxHashMap::default()) } fn handle_rustc_crates( crate_graph: &mut CrateGraph, + proc_macros: &mut ProcMacros, pkg_to_lib_crate: &mut FxHashMap, load: &mut dyn FnMut(&AbsPath) -> Option, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, @@ -1075,6 +1080,7 @@ fn handle_rustc_crates( if let Some(file_id) = load(&rustc_workspace[tgt].root) { let crate_id = add_target_crate_root( crate_graph, + proc_macros, &rustc_workspace[pkg], build_scripts.get_output(pkg), cfg_options.clone(), @@ -1140,6 +1146,7 @@ fn handle_rustc_crates( fn add_target_crate_root( crate_graph: &mut CrateGraph, + proc_macros: &mut ProcMacros, pkg: &PackageData, build_data: Option<&BuildScriptOutput>, cfg_options: CfgOptions, @@ -1176,14 +1183,8 @@ fn add_target_crate_root( } } - let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { - Some(Some(it)) => load_proc_macro(it), - Some(None) => Err("no proc macro dylib present".into()), - None => Err("crate has not (yet) been built".into()), - }; - let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); - crate_graph.add_crate_root( + let crate_id = crate_graph.add_crate_root( file_id, edition, Some(display_name), @@ -1191,11 +1192,19 @@ fn add_target_crate_root( cfg_options, potential_cfg_options, env, - proc_macro, is_proc_macro, CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }, target_layout, - ) + ); + let proc_macro = match build_data.as_ref().map(|it| &it.proc_macro_dylib_path) { + Some(it) => it.as_deref().map(load_proc_macro), + None => Some(Err("crate has not (yet) been built".into())), + }; + if let Some(proc_macro) = proc_macro { + proc_macros.insert(crate_id, proc_macro); + } + + crate_id } #[derive(Default)] @@ -1237,7 +1246,6 @@ fn sysroot_to_crate_graph( cfg_options.clone(), cfg_options.clone(), env, - Err("no proc macro loaded for sysroot crate".into()), false, CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), target_layout.clone(), diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 5a958d963e4b5..2d15d673edb04 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -6,7 +6,10 @@ use anyhow::Result; use crossbeam_channel::{unbounded, Receiver}; use hir::db::DefDatabase; use ide::{AnalysisHost, Change}; -use ide_db::{base_db::CrateGraph, FxHashMap}; +use ide_db::{ + base_db::{CrateGraph, ProcMacros}, + FxHashMap, +}; use proc_macro_api::ProcMacroServer; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; use vfs::{loader::Handle, AbsPath, AbsPathBuf}; @@ -79,7 +82,7 @@ pub fn load_workspace( ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()), }; - let crate_graph = ws.to_crate_graph( + let (crate_graph, proc_macros) = ws.to_crate_graph( &mut |_, path: &AbsPath| { load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[]) }, @@ -100,8 +103,13 @@ pub fn load_workspace( }); tracing::debug!("crate graph: {:?}", crate_graph); - let host = - load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); + let host = load_crate_graph( + crate_graph, + proc_macros, + project_folders.source_root_config, + &mut vfs, + &receiver, + ); if load_config.prefill_caches { host.analysis().parallel_prime_caches(1, |_| {})?; @@ -111,6 +119,7 @@ pub fn load_workspace( fn load_crate_graph( crate_graph: CrateGraph, + proc_macros: ProcMacros, source_root_config: SourceRootConfig, vfs: &mut vfs::Vfs, receiver: &Receiver, @@ -149,6 +158,7 @@ fn load_crate_graph( analysis_change.set_roots(source_roots); analysis_change.set_crate_graph(crate_graph); + analysis_change.set_proc_macros(proc_macros); host.apply_change(analysis_change); host diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 138b8446b9082..65758419deacb 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -20,7 +20,7 @@ use ide::Change; use ide_db::{ base_db::{ CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, - ProcMacroLoadResult, SourceRoot, VfsPath, + ProcMacroLoadResult, ProcMacros, SourceRoot, VfsPath, }, FxHashMap, }; @@ -355,7 +355,7 @@ impl GlobalState { }); // Create crate graph from all the workspaces - let crate_graph = { + let (crate_graph, proc_macros) = { let dummy_replacements = self.config.dummy_replacements(); let vfs = &mut self.vfs.write().0; @@ -376,6 +376,7 @@ impl GlobalState { }; let mut crate_graph = CrateGraph::default(); + let mut proc_macros = ProcMacros::default(); for (idx, ws) in self.workspaces.iter().enumerate() { let proc_macro_client = match self.proc_macro_clients.get(idx) { Some(res) => res.as_ref().map_err(|e| &**e), @@ -388,15 +389,17 @@ impl GlobalState { dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), ) }; - crate_graph.extend(ws.to_crate_graph( + let (other, other_proc_macros) = ws.to_crate_graph( &mut load_proc_macro, &mut load, &self.config.cargo().extra_env, - )); + ); + crate_graph.extend(other, &mut proc_macros, other_proc_macros); } - crate_graph + (crate_graph, proc_macros) }; change.set_crate_graph(crate_graph); + change.set_proc_macros(proc_macros); self.source_root_config = project_folders.source_root_config; From e9fb2ffe4572471588b6bd602521a1421baf4dc3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 16:47:41 +0100 Subject: [PATCH 029/806] Add lsp command for rebuilding proc macros --- crates/rust-analyzer/src/handlers.rs | 8 ++++++++ crates/rust-analyzer/src/lsp_ext.rs | 8 ++++++++ crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/src/commands.ts | 4 ++++ editors/code/src/ctx.ts | 5 ++++- editors/code/src/lsp_ext.ts | 1 + editors/code/src/main.ts | 1 + 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 2fca2ab851d41..a56c245dca579 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -52,6 +52,14 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } +pub(crate) fn handle_proc_macros_reload(state: &mut GlobalState, _: ()) -> Result<()> { + state.proc_macro_clients.clear(); + state.proc_macro_changed = false; + + state.fetch_build_data_queue.request_op("reload proc macros request".to_string()); + Ok(()) +} + pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { let _p = profile::span("handle_stop_flycheck"); state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index c7b513db981ea..2f8829ec7386f 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -51,6 +51,14 @@ impl Request for ReloadWorkspace { const METHOD: &'static str = "rust-analyzer/reloadWorkspace"; } +pub enum ReloadProcMacros {} + +impl Request for ReloadProcMacros { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/reloadProcMacros"; +} + pub enum SyntaxTree {} impl Request for SyntaxTree { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 67a54cde68c6f..ae7457e3473e2 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -633,6 +633,7 @@ impl GlobalState { dispatcher .on_sync_mut::(handlers::handle_workspace_reload) + .on_sync_mut::(handlers::handle_proc_macros_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 8a953577e99d3..8ce3466ed4400 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -749,6 +749,10 @@ export function reloadWorkspace(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.reloadWorkspace); } +export function reloadProcMacros(ctx: CtxInit): Cmd { + return async () => ctx.client.sendRequest(ra.reloadProcMacros); +} + export function addProject(ctx: CtxInit): Cmd { return async () => { const discoverProjectCommand = ctx.config.discoverProjectCommand; diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 85579453a63d7..8da8b0d63a151 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -378,10 +378,13 @@ export class Ctx { if (statusBar.tooltip.value) { statusBar.tooltip.appendText("\n\n"); } + statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)" ); - statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown( + "\n\n[Rebuild Proc Macros](command:rust-analyzer.reloadProcMacros)" + ); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 872d7199b838a..a03777d1c0af4 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -43,6 +43,7 @@ export const relatedTests = new lc.RequestType("rust-analyzer/reloadWorkspace"); +export const reloadProcMacros = new lc.RequestType0("rust-analyzer/reloadProcMacros"); export const runFlycheck = new lc.NotificationType<{ textDocument: lc.TextDocumentIdentifier | null; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index d5de00561b123..7079f235cab1c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -153,6 +153,7 @@ function createCommands(): Record { memoryUsage: { enabled: commands.memoryUsage }, shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, + reloadProcMacros: { enabled: commands.reloadProcMacros }, addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, From 607375dc206259955a386c48d88c45529c316145 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 18:06:06 +0100 Subject: [PATCH 030/806] Load proc-macros asynchronously --- crates/base-db/src/input.rs | 21 ++--- crates/base-db/src/lib.rs | 4 +- crates/project-model/src/tests.rs | 14 +-- crates/project-model/src/workspace.rs | 41 +++----- crates/rust-analyzer/src/cli/load_cargo.rs | 22 ++++- crates/rust-analyzer/src/global_state.rs | 10 +- crates/rust-analyzer/src/handlers.rs | 5 +- crates/rust-analyzer/src/main_loop.rs | 18 +++- crates/rust-analyzer/src/reload.rs | 105 +++++++++++++++------ docs/dev/lsp-extensions.md | 2 +- 10 files changed, 154 insertions(+), 88 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 41a2abd803a62..9580b76faa221 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -6,15 +6,16 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; +use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; use cfg::CfgOptions; use rustc_hash::FxHashMap; use stdx::hash::{NoHashHashMap, NoHashHashSet}; use syntax::SmolStr; use tt::token_id::Subtree; -use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath}; +use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; +pub type ProcMacroPaths = FxHashMap, AbsPathBuf), String>>; pub type ProcMacros = FxHashMap; /// Files are grouped into source roots. A source root is a directory on the @@ -455,16 +456,11 @@ impl CrateGraph { } /// Extends this crate graph by adding a complete disjoint second crate - /// graph. + /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// /// The ids of the crates in the `other` graph are shifted by the return /// amount. - pub fn extend( - &mut self, - other: CrateGraph, - proc_macros: &mut ProcMacros, - other_proc_macros: ProcMacros, - ) -> u32 { + pub fn extend(&mut self, other: CrateGraph, proc_macros: &mut ProcMacroPaths) -> u32 { let start = self.arena.len() as u32; self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { let new_id = id.shift(start); @@ -473,8 +469,11 @@ impl CrateGraph { } (new_id, data) })); - proc_macros - .extend(other_proc_macros.into_iter().map(|(id, macros)| (id.shift(start), macros))); + + *proc_macros = mem::take(proc_macros) + .into_iter() + .map(|(id, macros)| (id.shift(start), macros)) + .collect(); start } diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 7ab9aa8709c04..f6975f2fbd75a 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -16,8 +16,8 @@ pub use crate::{ input::{ CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacros, SourceRoot, SourceRootId, - TargetLayoutLoadResult, + ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, + SourceRootId, TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index ed78d71a1a57a..26c4c89f7646c 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use base_db::{CrateGraph, FileId, ProcMacros}; +use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect, Expect}; use paths::{AbsPath, AbsPathBuf}; @@ -14,11 +14,14 @@ use crate::{ WorkspaceBuildScripts, }; -fn load_cargo(file: &str) -> (CrateGraph, ProcMacros) { +fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { load_cargo_with_overrides(file, CfgOverrides::default()) } -fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> (CrateGraph, ProcMacros) { +fn load_cargo_with_overrides( + file: &str, + cfg_overrides: CfgOverrides, +) -> (CrateGraph, ProcMacroPaths) { let meta = get_test_json_file(file); let cargo_workspace = CargoWorkspace::new(meta); let project_workspace = ProjectWorkspace::Cargo { @@ -34,7 +37,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> (CrateG to_crate_graph(project_workspace) } -fn load_rust_project(file: &str) -> (CrateGraph, ProcMacros) { +fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); let sysroot = Ok(get_fake_sysroot()); @@ -92,9 +95,8 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { ProjectJson::new(base, data) } -fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacros) { +fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) { project_workspace.to_crate_graph( - &mut |_, _| Ok(Vec::new()), &mut { let mut counter = 0; move |_path| { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 5766859143b20..1fd7c681938e6 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc}; use anyhow::{bail, format_err, Context, Result}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroLoadResult, ProcMacros, TargetLayoutLoadResult, + FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; @@ -576,16 +576,14 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, extra_env: &FxHashMap, - ) -> (CrateGraph, ProcMacros) { + ) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("ProjectWorkspace::to_crate_graph"); let (mut crate_graph, proc_macros) = match self { ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph( rustc_cfg.clone(), - load_proc_macro, load, project, sysroot.as_ref().ok(), @@ -602,7 +600,6 @@ impl ProjectWorkspace { toolchain: _, target_layout, } => cargo_to_crate_graph( - load_proc_macro, load, rustc.as_ref().ok(), cargo, @@ -679,15 +676,14 @@ impl ProjectWorkspace { fn project_json_to_crate_graph( rustc_cfg: Vec, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, project: &ProjectJson, sysroot: Option<&Sysroot>, extra_env: &FxHashMap, target_layout: TargetLayoutLoadResult, -) -> (CrateGraph, ProcMacros) { +) -> (CrateGraph, ProcMacroPaths) { let mut crate_graph = CrateGraph::default(); - let mut proc_macros = FxHashMap::<_, _>::default(); + let mut proc_macros = FxHashMap::default(); let sysroot_deps = sysroot.as_ref().map(|sysroot| { sysroot_to_crate_graph( &mut crate_graph, @@ -708,16 +704,15 @@ fn project_json_to_crate_graph( }) .map(|(crate_id, krate, file_id)| { let env = krate.env.clone().into_iter().collect(); - if let Some(it) = krate.proc_macro_dylib_path.clone() { + if let Some(path) = krate.proc_macro_dylib_path.clone() { proc_macros.insert( crate_id, - load_proc_macro( - krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""), - &it, - ), + Ok(( + krate.display_name.as_ref().map(|it| it.canonical_name().to_owned()), + path, + )), ); } - let target_cfgs = match krate.target.as_deref() { Some(target) => cfg_cache .entry(target) @@ -782,7 +777,6 @@ 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<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, @@ -791,7 +785,7 @@ fn cargo_to_crate_graph( override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, -) -> (CrateGraph, ProcMacros) { +) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let mut proc_macros = FxHashMap::default(); @@ -862,7 +856,6 @@ fn cargo_to_crate_graph( &cargo[pkg], build_scripts.get_output(pkg), cfg_options.clone(), - &mut |path| load_proc_macro(&cargo[tgt].name, path), file_id, &cargo[tgt].name, cargo[tgt].is_proc_macro, @@ -938,7 +931,6 @@ fn cargo_to_crate_graph( &mut proc_macros, &mut pkg_to_lib_crate, load, - load_proc_macro, rustc_workspace, cargo, &public_deps, @@ -966,7 +958,7 @@ fn detached_files_to_crate_graph( detached_files: &[AbsPathBuf], sysroot: Option<&Sysroot>, target_layout: TargetLayoutLoadResult, -) -> (CrateGraph, ProcMacros) { +) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("detached_files_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, _libproc_macro) = match sysroot { @@ -1018,10 +1010,9 @@ fn detached_files_to_crate_graph( fn handle_rustc_crates( crate_graph: &mut CrateGraph, - proc_macros: &mut ProcMacros, + proc_macros: &mut ProcMacroPaths, pkg_to_lib_crate: &mut FxHashMap, load: &mut dyn FnMut(&AbsPath) -> Option, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, rustc_workspace: &CargoWorkspace, cargo: &CargoWorkspace, public_deps: &SysrootPublicDeps, @@ -1084,7 +1075,6 @@ fn handle_rustc_crates( &rustc_workspace[pkg], build_scripts.get_output(pkg), cfg_options.clone(), - &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path), file_id, &rustc_workspace[tgt].name, rustc_workspace[tgt].is_proc_macro, @@ -1146,11 +1136,10 @@ fn handle_rustc_crates( fn add_target_crate_root( crate_graph: &mut CrateGraph, - proc_macros: &mut ProcMacros, + proc_macros: &mut ProcMacroPaths, pkg: &PackageData, build_data: Option<&BuildScriptOutput>, cfg_options: CfgOptions, - load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult, file_id: FileId, cargo_name: &str, is_proc_macro: bool, @@ -1197,11 +1186,11 @@ fn add_target_crate_root( target_layout, ); let proc_macro = match build_data.as_ref().map(|it| &it.proc_macro_dylib_path) { - Some(it) => it.as_deref().map(load_proc_macro), + Some(it) => it.clone().map(Ok), None => Some(Err("crate has not (yet) been built".into())), }; if let Some(proc_macro) = proc_macro { - proc_macros.insert(crate_id, proc_macro); + proc_macros.insert(crate_id, proc_macro.map(|path| (Some(cargo_name.to_owned()), path))); } crate_id diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 2d15d673edb04..f5bc3c12c1491 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -69,7 +69,7 @@ pub fn load_workspace( Box::new(loader) }; - let proc_macro_client = match &load_config.with_proc_macro_server { + let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() .ok_or_else(|| "failed to find sysroot proc-macro server".to_owned()) @@ -83,9 +83,6 @@ pub fn load_workspace( }; let (crate_graph, proc_macros) = ws.to_crate_graph( - &mut |_, path: &AbsPath| { - load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[]) - }, &mut |path: &AbsPath| { let contents = loader.load_sync(path); let path = vfs::VfsPath::from(path.to_path_buf()); @@ -94,6 +91,21 @@ pub fn load_workspace( }, extra_env, ); + let proc_macros = { + let proc_macro_server = match &proc_macro_server { + Ok(it) => Ok(it), + Err(e) => Err(e.as_str()), + }; + proc_macros + .into_iter() + .map(|(crate_id, path)| { + ( + crate_id, + path.and_then(|(_, path)| load_proc_macro(proc_macro_server, &path, &[])), + ) + }) + .collect() + }; let project_folders = ProjectFolders::new(&[ws], &[]); loader.set_config(vfs::loader::Config { @@ -114,7 +126,7 @@ pub fn load_workspace( if load_config.prefill_caches { host.analysis().parallel_prime_caches(1, |_| {})?; } - Ok((host, vfs, proc_macro_client.ok())) + Ok((host, vfs, proc_macro_server.ok())) } fn load_crate_graph( diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index aca6c92357070..d02714ad1ead0 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -59,10 +59,11 @@ pub(crate) struct GlobalState { pub(crate) mem_docs: MemDocs, pub(crate) semantic_tokens_cache: Arc>>, pub(crate) shutdown_requested: bool, - pub(crate) proc_macro_changed: bool, pub(crate) last_reported_status: Option, pub(crate) source_root_config: SourceRootConfig, - pub(crate) proc_macro_clients: Vec>, + + pub(crate) proc_macro_changed: bool, + pub(crate) proc_macro_clients: Arc<[Result]>, pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck_sender: Sender, @@ -151,10 +152,11 @@ impl GlobalState { mem_docs: MemDocs::default(), semantic_tokens_cache: Arc::new(Default::default()), shutdown_requested: false, - proc_macro_changed: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), - proc_macro_clients: vec![], + + proc_macro_changed: false, + proc_macro_clients: Arc::new([]), flycheck: Arc::new([]), flycheck_sender, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index a56c245dca579..8866515bb94dd 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -5,6 +5,7 @@ use std::{ io::Write as _, process::{self, Stdio}, + sync::Arc, }; use anyhow::Context; @@ -44,7 +45,7 @@ use crate::{ }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { - state.proc_macro_clients.clear(); + state.proc_macro_clients = Arc::new([]); state.proc_macro_changed = false; state.fetch_workspaces_queue.request_op("reload workspace request".to_string()); @@ -53,7 +54,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< } pub(crate) fn handle_proc_macros_reload(state: &mut GlobalState, _: ()) -> Result<()> { - state.proc_macro_clients.clear(); + state.proc_macro_clients = Arc::new([]); state.proc_macro_changed = false; state.fetch_build_data_queue.request_op("reload proc macros request".to_string()); diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ae7457e3473e2..8db526e0b76df 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -24,7 +24,7 @@ use crate::{ handlers, lsp_ext, lsp_utils::{apply_document_changes, notification_is, Progress}, mem_docs::DocumentData, - reload::{self, BuildDataProgress, ProjectWorkspaceProgress}, + reload::{self, BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, Result, }; @@ -68,6 +68,7 @@ pub(crate) enum Task { PrimeCaches(PrimeCachesProgress), FetchWorkspace(ProjectWorkspaceProgress), FetchBuildData(BuildDataProgress), + LoadProcMacros(ProcMacroProgress), } #[derive(Debug)] @@ -487,6 +488,21 @@ impl GlobalState { } }; + if let Some(state) = state { + self.report_progress("Building", state, msg, None, None); + } + } + Task::LoadProcMacros(progress) => { + let (state, msg) = match progress { + ProcMacroProgress::Begin => (Some(Progress::Begin), None), + ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)), + ProcMacroProgress::End(proc_macro_load_result) => { + self.set_proc_macros(proc_macro_load_result); + + (Some(Progress::End), None) + } + }; + if let Some(state) = state { self.report_progress("Loading", state, msg, None, None); } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 65758419deacb..f8f2cb093220d 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -12,7 +12,7 @@ //! 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::{collections::hash_map::Entry, mem, sync::Arc}; +use std::{collections::hash_map::Entry, iter, mem, sync::Arc}; use flycheck::{FlycheckConfig, FlycheckHandle}; use hir::db::DefDatabase; @@ -20,7 +20,7 @@ use ide::Change; use ide_db::{ base_db::{ CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, - ProcMacroLoadResult, ProcMacros, SourceRoot, VfsPath, + ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, VfsPath, }, FxHashMap, }; @@ -54,6 +54,13 @@ pub(crate) enum BuildDataProgress { End((Arc>, Vec>)), } +#[derive(Debug)] +pub(crate) enum ProcMacroProgress { + Begin, + Report(String), + End(ProcMacros), +} + impl GlobalState { pub(crate) fn is_quiescent(&self) -> bool { !(self.last_reported_status.is_none() @@ -216,6 +223,59 @@ impl GlobalState { }); } + pub(crate) fn load_proc_macros(&mut self, paths: Vec) { + tracing::info!("will load proc macros"); + let dummy_replacements = self.config.dummy_replacements().clone(); + let proc_macro_clients = self.proc_macro_clients.clone(); + + self.task_pool.handle.spawn_with_sender(move |sender| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); + + let dummy_replacements = &dummy_replacements; + let progress = { + let sender = sender.clone(); + &move |msg| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() + } + }; + + let mut res = FxHashMap::default(); + for (client, paths) in proc_macro_clients + .iter() + .map(|res| res.as_ref().map_err(|e| &**e)) + .chain(iter::repeat_with(|| Err("Proc macros are disabled"))) + .zip(paths) + { + res.extend(paths.into_iter().map(move |(crate_id, res)| { + ( + crate_id, + res.and_then(|(crate_name, path)| { + progress(path.display().to_string()); + load_proc_macro( + client, + &path, + crate_name + .as_deref() + .and_then(|crate_name| { + dummy_replacements.get(crate_name).map(|v| &**v) + }) + .unwrap_or_default(), + ) + }), + ) + })); + } + + sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); + }); + } + + pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) { + let mut change = Change::new(); + change.set_proc_macros(proc_macros); + self.analysis_host.apply_change(change); + } + pub(crate) fn switch_workspaces(&mut self, cause: Cause) { let _p = profile::span("GlobalState::switch_workspaces"); tracing::info!(%cause, "will switch workspaces"); @@ -303,8 +363,6 @@ impl GlobalState { ); } - let mut change = Change::new(); - let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); @@ -353,11 +411,10 @@ impl GlobalState { watch, version: self.vfs_config_version, }); + self.source_root_config = project_folders.source_root_config; // Create crate graph from all the workspaces - let (crate_graph, proc_macros) = { - let dummy_replacements = self.config.dummy_replacements(); - + let (crate_graph, proc_macro_paths) = { let vfs = &mut self.vfs.write().0; let loader = &mut self.loader; let mem_docs = &self.mem_docs; @@ -376,34 +433,22 @@ impl GlobalState { }; let mut crate_graph = CrateGraph::default(); - let mut proc_macros = ProcMacros::default(); - for (idx, ws) in self.workspaces.iter().enumerate() { - let proc_macro_client = match self.proc_macro_clients.get(idx) { - Some(res) => res.as_ref().map_err(|e| &**e), - None => Err("Proc macros are disabled"), - }; - let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { - load_proc_macro( - proc_macro_client, - path, - dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), - ) - }; - let (other, other_proc_macros) = ws.to_crate_graph( - &mut load_proc_macro, - &mut load, - &self.config.cargo().extra_env, - ); - crate_graph.extend(other, &mut proc_macros, other_proc_macros); + let mut proc_macros = Vec::default(); + for ws in &**self.workspaces { + let (other, mut crate_proc_macros) = + ws.to_crate_graph(&mut load, &self.config.cargo().extra_env); + crate_graph.extend(other, &mut crate_proc_macros); + proc_macros.push(crate_proc_macros); } (crate_graph, proc_macros) }; + let mut change = Change::new(); change.set_crate_graph(crate_graph); - change.set_proc_macros(proc_macros); - - self.source_root_config = project_folders.source_root_config; - self.analysis_host.apply_change(change); + + if same_workspaces { + self.load_proc_macros(proc_macro_paths); + } self.process_changes(); self.reload_flycheck(); tracing::info!("did switch workspaces"); diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index de1422032088f..11eda94f5bb64 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $SRC_DIR/core/src/cmp.rs:LL:COL +help: consider annotating `A` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | enum A { + | help: use parentheses to construct this tuple variant | LL | a == A::Value(/* () */); diff --git a/tests/ui/typeck/derive-sugg-arg-arity.rs b/tests/ui/typeck/derive-sugg-arg-arity.rs new file mode 100644 index 0000000000000..094c93a8535d8 --- /dev/null +++ b/tests/ui/typeck/derive-sugg-arg-arity.rs @@ -0,0 +1,8 @@ +pub struct A; + +fn main() { + match () { + _ => match A::partial_cmp() {}, + //~^ ERROR the function or associated item `partial_cmp` exists for struct `A`, but its trait bounds were not satisfied + } +} diff --git a/tests/ui/typeck/derive-sugg-arg-arity.stderr b/tests/ui/typeck/derive-sugg-arg-arity.stderr new file mode 100644 index 0000000000000..5b4c481719830 --- /dev/null +++ b/tests/ui/typeck/derive-sugg-arg-arity.stderr @@ -0,0 +1,31 @@ +error[E0599]: the function or associated item `partial_cmp` exists for struct `A`, but its trait bounds were not satisfied + --> $DIR/derive-sugg-arg-arity.rs:5:23 + | +LL | pub struct A; + | ------------ + | | + | function or associated item `partial_cmp` not found for this struct + | doesn't satisfy `A: Iterator` + | doesn't satisfy `A: PartialOrd<_>` +... +LL | _ => match A::partial_cmp() {}, + | ^^^^^^^^^^^ function or associated item cannot be called on `A` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `A: PartialOrd<_>` + which is required by `&A: PartialOrd<&_>` + `A: PartialOrd<_>` + which is required by `&mut A: PartialOrd<&mut _>` + `A: Iterator` + which is required by `&mut A: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]` + | +LL + #[derive(PartialEq, PartialOrd)] +LL | pub struct A; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. From 730286b523c23170630142b6ebba0677a9aedae9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 10:42:26 +0200 Subject: [PATCH 399/806] Restructure InlayHint, no longer derive properties from its kind --- crates/ide/src/inlay_hints.rs | 47 +-- crates/ide/src/inlay_hints/adjustment.rs | 41 ++- crates/ide/src/inlay_hints/bind_pat.rs | 310 ++++++++---------- crates/ide/src/inlay_hints/binding_mode.rs | 18 +- crates/ide/src/inlay_hints/chaining.rs | 57 +++- crates/ide/src/inlay_hints/closing_brace.rs | 5 +- .../ide/src/inlay_hints/closure_captures.rs | 17 +- crates/ide/src/inlay_hints/closure_ret.rs | 56 +++- crates/ide/src/inlay_hints/discriminant.rs | 91 ++--- crates/ide/src/inlay_hints/fn_lifetime_fn.rs | 11 +- crates/ide/src/inlay_hints/implicit_static.rs | 5 +- crates/ide/src/inlay_hints/param_name.rs | 16 +- crates/ide/src/lib.rs | 4 +- crates/rust-analyzer/src/handlers/request.rs | 4 +- crates/rust-analyzer/src/to_proto.rs | 82 +---- 15 files changed, 416 insertions(+), 348 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index c326688ae6a9e..292591674191c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -90,32 +90,34 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -// FIXME: Clean up this mess, the kinds are mainly used for setting different rendering properties in the lsp layer -// We should probably turns this into such a property holding struct. Or clean this up in some other form. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InlayKind { + Adjustment, BindingMode, Chaining, ClosingBrace, - ClosureReturnType, + ClosureCapture, + Discriminant, GenericParamList, - Adjustment, - AdjustmentPostfix, Lifetime, - ClosureCapture, Parameter, Type, - Discriminant, - OpeningParenthesis, - ClosingParenthesis, +} + +#[derive(Debug)] +pub enum InlayHintPosition { + Before, + After, } #[derive(Debug)] pub struct InlayHint { /// The text range this inlay hint applies to. pub range: TextRange, - /// The kind of this inlay hint. This is used to determine side and padding of the hint for - /// rendering purposes. + pub position: InlayHintPosition, + pub pad_left: bool, + pub pad_right: bool, + /// The kind of this inlay hint. pub kind: InlayKind, /// The actual label to show in the inlay hint. pub label: InlayHintLabel, @@ -124,20 +126,26 @@ pub struct InlayHint { } impl InlayHint { - fn closing_paren(range: TextRange) -> InlayHint { + fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { range, - kind: InlayKind::ClosingParenthesis, + kind, label: InlayHintLabel::from(")"), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, } } - fn opening_paren(range: TextRange) -> InlayHint { + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { range, - kind: InlayKind::OpeningParenthesis, + kind, label: InlayHintLabel::from("("), text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: false, } } } @@ -303,13 +311,13 @@ impl InlayHintLabelBuilder<'_> { fn label_of_ty( famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - ty: hir::Type, + ty: &hir::Type, ) -> Option { fn rec( sema: &Semantics<'_, RootDatabase>, famous_defs: &FamousDefs<'_, '_>, mut max_length: Option, - ty: hir::Type, + ty: &hir::Type, label_builder: &mut InlayHintLabelBuilder<'_>, config: &InlayHintsConfig, ) -> Result<(), HirDisplayError> { @@ -342,7 +350,7 @@ fn label_of_ty( label_builder.write_str(LABEL_ITEM)?; label_builder.end_location_link(); label_builder.write_str(LABEL_MIDDLE2)?; - rec(sema, famous_defs, max_length, ty, label_builder, config)?; + rec(sema, famous_defs, max_length, &ty, label_builder, config)?; label_builder.write_str(LABEL_END)?; Ok(()) } @@ -574,7 +582,8 @@ mod tests { let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); let actual = inlay_hints .into_iter() - .map(|it| (it.range, it.label.to_string())) + // FIXME: We trim the start because some inlay produces leading whitespace which is not properly supported by our annotation extraction + .map(|it| (it.range, it.label.to_string().trim_start().to_owned())) .sorted_by_key(|(range, _)| range.start()) .collect::>(); expected.sort_by_key(|(range, _)| range.start()); diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 27e9ba3c36b4a..10bee2a6accfe 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -3,6 +3,7 @@ //! let _: u32 = /* */ loop {}; //! let _: &u32 = /* &* */ &mut 0; //! ``` +use either::Either; use hir::{ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, Semantics, @@ -16,8 +17,8 @@ use syntax::{ }; use crate::{ - AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, - InlayTooltip, + AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition, + InlayHintsConfig, InlayKind, InlayTooltip, }; pub(super) fn hints( @@ -63,22 +64,26 @@ pub(super) fn hints( mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode); if needs_outer_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); } if postfix && needs_inner_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } - let (mut tmp0, mut tmp1); - let iter: &mut dyn Iterator = if postfix { - tmp0 = adjustments.into_iter(); - &mut tmp0 + let mut iter = if postfix { + Either::Left(adjustments.into_iter()) } else { - tmp1 = adjustments.into_iter().rev(); - &mut tmp1 + Either::Right(adjustments.into_iter().rev()) }; + let iter: &mut dyn Iterator = iter.as_mut().either(|it| it as _, |it| it as _); for Adjustment { source, target, kind } in iter { if source == target { @@ -134,7 +139,10 @@ pub(super) fn hints( }; acc.push(InlayHint { range: expr.syntax().text_range(), - kind: if postfix { InlayKind::AdjustmentPostfix } else { InlayKind::Adjustment }, + pad_left: false, + pad_right: false, + position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before }, + kind: InlayKind::Adjustment, label: InlayHintLabel::simple( if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, Some(InlayTooltip::Markdown(format!( @@ -148,11 +156,14 @@ pub(super) fn hints( }); } if !postfix && needs_inner_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } if needs_outer_parens { - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } Some(()) } diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 6991a66c7c211..f98cc129a9386 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -14,7 +14,7 @@ use syntax::{ use crate::{ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, - InlayHint, InlayHintsConfig, InlayKind, + InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, }; pub(super) fn hints( @@ -36,7 +36,7 @@ pub(super) fn hints( return None; } - let label = label_of_ty(famous_defs, config, ty.clone())?; + let mut label = label_of_ty(famous_defs, config, &ty)?; if config.hide_named_constructor_hints && is_named_constructor(sema, pat, &label.to_string()).is_some() @@ -44,31 +44,46 @@ pub(super) fn hints( return None; } - let type_annotation_is_valid = desc_pat - .syntax() - .parent() - .map(|it| ast::LetStmt::can_cast(it.kind()) || ast::Param::can_cast(it.kind())) - .unwrap_or(false); - let text_edit = if type_annotation_is_valid { + let type_ascriptable = desc_pat.syntax().parent().and_then(|it| { + ast::LetStmt::cast(it.clone()) + .map(|it| it.colon_token()) + .or_else(|| ast::Param::cast(it).map(|it| it.colon_token())) + }); + let text_edit = if let Some(colon_token) = &type_ascriptable { ty_to_text_edit( sema, desc_pat.syntax(), &ty, - pat.syntax().text_range().end(), - String::from(": "), + colon_token + .as_ref() + .map_or_else(|| pat.syntax().text_range(), |t| t.text_range()) + .end(), + if colon_token.is_some() { String::new() } else { String::from(": ") }, ) } else { None }; + let has_colon = matches!(type_ascriptable, Some(Some(_))) && !config.render_colons; + if !has_colon { + label.prepend_str(": "); + } + + let text_range = match pat.name() { + Some(name) => name.syntax().text_range(), + None => pat.syntax().text_range(), + }; acc.push(InlayHint { - range: match pat.name() { - Some(name) => name.syntax().text_range(), - None => pat.syntax().text_range(), + range: match type_ascriptable { + Some(Some(t)) => text_range.cover(t.text_range()), + _ => text_range, }, kind: InlayKind::Type, label, text_edit, + position: InlayHintPosition::Before, + pad_left: !has_colon, + pad_right: false, }); Some(()) @@ -218,7 +233,7 @@ mod tests { fn foo(a: i32, b: i32) -> i32 { a + b } fn main() { let _x = foo(4, 4); - //^^ i32 + //^^ : i32 }"#, ); } @@ -230,17 +245,17 @@ fn main() { //- minicore: option fn main() { let ref foo @ bar @ ref mut baz = 0; - //^^^ &i32 - //^^^ i32 - //^^^ &mut i32 + //^^^ : &i32 + //^^^ : i32 + //^^^ : &mut i32 let [x @ ..] = [0]; - //^ [i32; 1] + //^ : [i32; 1] if let x @ Some(_) = Some(0) {} - //^ Option + //^ : Option let foo @ (bar, baz) = (3, 3); - //^^^ (i32, i32) - //^^^ i32 - //^^^ i32 + //^^^ : (i32, i32) + //^^^ : i32 + //^^^ : i32 }"#, ); } @@ -253,11 +268,11 @@ struct Test { k: K, t: T } fn main() { let zz = Test { t: 23u8, k: 33 }; - //^^ Test + //^^ : Test let zz_ref = &zz; - //^^^^^^ &Test + //^^^^^^ : &Test let test = || zz; - //^^^^ impl FnOnce() -> Test + //^^^^ : impl FnOnce() -> Test }"#, ); } @@ -285,10 +300,10 @@ impl Iterator for SomeIter { fn main() { let mut some_iter = SomeIter::new(); - //^^^^^^^^^ SomeIter>> + //^^^^^^^^^ : SomeIter>> some_iter.push(iter::repeat(2).take(2)); let iter_of_iters = some_iter.take(2); - //^^^^^^^^^^^^^ impl Iterator> + //^^^^^^^^^^^^^ : impl Iterator> } "#, ); @@ -347,7 +362,7 @@ fn main(a: SliceIter<'_, Container>) { pub fn quux() -> T::Bar { let y = Default::default(); - //^ ::Bar + //^ : ::Bar y } @@ -371,21 +386,21 @@ fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } fn main() { let foo = foo(); - // ^^^ impl Fn() + // ^^^ : impl Fn() let foo = foo1(); - // ^^^ impl Fn(f64) + // ^^^ : impl Fn(f64) let foo = foo2(); - // ^^^ impl Fn(f64, f64) + // ^^^ : impl Fn(f64, f64) let foo = foo3(); - // ^^^ impl Fn(f64, f64) -> u32 + // ^^^ : impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &dyn Fn(f64, f64) -> u32 + // ^^^ : &dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + // ^^^ : &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); - // ^^^ impl Fn(f64, f64) -> u32 + // ^^^ : impl Fn(f64, f64) -> u32 let foo = foo7(); - // ^^^ *const impl Fn(f64, f64) -> u32 + // ^^^ : *const impl Fn(f64, f64) -> u32 } "#, ) @@ -408,9 +423,9 @@ fn main() { let foo = foo(); let foo = foo1(); let foo = foo2(); - // ^^^ impl Fn(f64, f64) + // ^^^ : impl Fn(f64, f64) let foo = foo3(); - // ^^^ impl Fn(f64, f64) -> u32 + // ^^^ : impl Fn(f64, f64) -> u32 let foo = foo4(); let foo = foo5(); let foo = foo6(); @@ -451,25 +466,25 @@ fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} } fn main() { let foo = foo1(); - // ^^^ *const impl Fn() + // ^^^ : *const impl Fn() let foo = foo2(); - // ^^^ *const impl Fn() + // ^^^ : *const impl Fn() let foo = foo3(); - // ^^^ *const (impl Fn() + ?Sized) + // ^^^ : *const (impl Fn() + ?Sized) let foo = foo4(); - // ^^^ *const impl Fn() + // ^^^ : *const impl Fn() let foo = foo5(); - // ^^^ *const (impl Fn() + ?Sized) + // ^^^ : *const (impl Fn() + ?Sized) let foo = foo6(); - // ^^^ *const (impl Fn() + Trait) + // ^^^ : *const (impl Fn() + Trait) let foo = foo7(); - // ^^^ *const (impl Fn() + Trait) + // ^^^ : *const (impl Fn() + Trait) let foo = foo8(); - // ^^^ *const (impl Fn() + Trait + ?Sized) + // ^^^ : *const (impl Fn() + Trait + ?Sized) let foo = foo9(); - // ^^^ *const (impl Fn() -> u8 + ?Sized) + // ^^^ : *const (impl Fn() -> u8 + ?Sized) let foo = foo10(); - // ^^^ *const impl Fn() + // ^^^ : *const impl Fn() } "#, ) @@ -520,24 +535,24 @@ fn main() { struct InnerStruct {} let test = 54; - //^^^^ i32 + //^^^^ : i32 let test: i32 = 33; let mut test = 33; - //^^^^ i32 + //^^^^ : i32 let _ = 22; let test = "test"; - //^^^^ &str + //^^^^ : &str let test = InnerStruct {}; - //^^^^ InnerStruct + //^^^^ : InnerStruct let test = unresolved(); let test = (42, 'a'); - //^^^^ (i32, char) - let (a, (b, (c,)) = (2, (3, (9.2,)); - //^ i32 ^ i32 ^ f64 + //^^^^ : (i32, char) + let (a, (b, (c,)) = (2, (3, (9.2,)); + //^ : i32 ^ : i32 ^ : f64 let &x = &92; - //^ i32 + //^ : i32 }"#, ); } @@ -551,22 +566,22 @@ struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); - //^^^^ Option + //^^^^ : Option if let None = &test {}; if let test = &test {}; - //^^^^ &Option + //^^^^ : &Option if let Some(test) = &test {}; - //^^^^ &Test - if let Some(Test { a, b }) = &test {}; - //^ &Option ^ &u8 - if let Some(Test { a: x, b: y }) = &test {}; - //^ &Option ^ &u8 - if let Some(Test { a: Some(x), b: y }) = &test {}; - //^ &u32 ^ &u8 + //^^^^ : &Test + if let Some(Test { a, b }) = &test {}; + //^ : &Option ^ : &u8 + if let Some(Test { a: x, b: y }) = &test {}; + //^ : &Option ^ : &u8 + if let Some(Test { a: Some(x), b: y }) = &test {}; + //^ : &u32 ^ : &u8 if let Some(Test { a: None, b: y }) = &test {}; - //^ &u8 + //^ : &u8 if let Some(Test { b: y, .. }) = &test {}; - //^ &u8 + //^ : &u8 if test == None {} }"#, ); @@ -581,9 +596,9 @@ struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); - //^^^^ Option - while let Some(Test { a: Some(x), b: y }) = &test {}; - //^ &u32 ^ &u8 + //^^^^ : Option + while let Some(Test { a: Some(x), b: y }) = &test {}; + //^ : &u32 ^ : &u8 }"#, ); } @@ -599,9 +614,9 @@ fn main() { match Some(Test { a: Some(3), b: 1 }) { None => (), test => (), - //^^^^ Option - Some(Test { a: Some(x), b: y }) => (), - //^ u32 ^ u8 + //^^^^ : Option + Some(Test { a: Some(x), b: y }) => (), + //^ : u32 ^ : u8 _ => {} } }"#, @@ -633,12 +648,12 @@ impl Iterator for IntoIter { fn main() { let mut data = Vec::new(); - //^^^^ Vec<&str> + //^^^^ : Vec<&str> data.push("foo"); for i in data { - //^ &str + //^ : &str let z = i; - //^ &str + //^ : &str } } "#, @@ -663,11 +678,11 @@ auto trait Sync {} fn main() { // The block expression wrapping disables the constructor hint hiding logic let _v = { Vec::>::new() }; - //^^ Vec> + //^^ : Vec> let _v = { Vec::>::new() }; - //^^ Vec> + //^^ : Vec> let _v = { Vec::>::new() }; - //^^ Vec> + //^^ : Vec> } "#, ); @@ -691,14 +706,14 @@ impl Iterator for MyIter { fn main() { let _x = MyIter; - //^^ MyIter + //^^ : MyIter let _x = iter::repeat(0); - //^^ impl Iterator + //^^ : impl Iterator fn generic(t: T) { let _x = iter::repeat(t); - //^^ impl Iterator + //^^ : impl Iterator let _chained = iter::repeat(t).take(10); - //^^^^^^^^ impl Iterator + //^^^^^^^^ : impl Iterator } } "#, @@ -762,20 +777,20 @@ fn main() { let tuple_struct = TupleStruct(); let generic0 = Generic::new(); - // ^^^^^^^^ Generic + // ^^^^^^^^ : Generic let generic1 = Generic(0); - // ^^^^^^^^ Generic + // ^^^^^^^^ : Generic let generic2 = Generic::::new(); let generic3 = >::new(); let generic4 = Generic::(0); let option = Some(0); - // ^^^^^^ Option + // ^^^^^^ : Option let func = times2; - // ^^^^ fn times2(i32) -> i32 + // ^^^^ : fn times2(i32) -> i32 let closure = |x: i32| x * 2; - // ^^^^^^^ impl Fn(i32) -> i32 + // ^^^^^^^ : impl Fn(i32) -> i32 } fn fallible() -> ControlFlow<()> { @@ -813,72 +828,25 @@ impl Generic { fn main() { let strukt = Struct::new(); - // ^^^^^^ Struct + // ^^^^^^ : Struct let tuple_struct = TupleStruct(); - // ^^^^^^^^^^^^ TupleStruct + // ^^^^^^^^^^^^ : TupleStruct let generic0 = Generic::new(); - // ^^^^^^^^ Generic + // ^^^^^^^^ : Generic let generic1 = Generic::::new(); - // ^^^^^^^^ Generic + // ^^^^^^^^ : Generic let generic2 = >::new(); - // ^^^^^^^^ Generic + // ^^^^^^^^ : Generic } fn fallible() -> ControlFlow<()> { let strukt = Struct::try_new()?; - // ^^^^^^ Struct + // ^^^^^^ : Struct } "#, ); } - #[test] - fn closures() { - check( - r#" -fn main() { - let mut start = 0; - //^^^^^ i32 - (0..2).for_each(|increment | { start += increment; }); - //^^^^^^^^^ i32 - - let multiply = - //^^^^^^^^ impl Fn(i32, i32) -> i32 - | a, b| a * b - //^ i32 ^ i32 - - ; - - let _: i32 = multiply(1, 2); - //^ a ^ b - let multiply_ref = &multiply; - //^^^^^^^^^^^^ &impl Fn(i32, i32) -> i32 - - let return_42 = || 42; - //^^^^^^^^^ impl Fn() -> i32 - || { 42 }; - //^^ i32 -}"#, - ); - } - - #[test] - fn return_type_hints_for_closure_without_block() { - check_with_config( - InlayHintsConfig { - closure_return_type_hints: ClosureReturnTypeHints::Always, - ..DISABLED_CONFIG - }, - r#" -fn main() { - let a = || { 0 }; - //^^ i32 - let b = || 0; - //^^ i32 -}"#, - ); - } - #[test] fn closure_style() { check_with_config( @@ -887,15 +855,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ impl Fn() -> i32 + //^ : impl Fn() -> i32 let y = |t: i32| x() + t; - //^ impl Fn(i32) -> i32 + //^ : impl Fn(i32) -> i32 let mut t = 5; - //^ i32 + //^ : i32 let z = |k: i32| { t += k; }; - //^ impl FnMut(i32) + //^ : impl FnMut(i32) let p = (y, z); - //^ (impl Fn(i32) -> i32, impl FnMut(i32)) + //^ : (impl Fn(i32) -> i32, impl FnMut(i32)) } "#, ); @@ -909,15 +877,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ || -> i32 + //^ : || -> i32 let y = |t: i32| x() + t; - //^ |i32| -> i32 + //^ : |i32| -> i32 let mut t = 5; - //^ i32 + //^ : i32 let z = |k: i32| { t += k; }; - //^ |i32| -> () + //^ : |i32| -> () let p = (y, z); - //^ (|i32| -> i32, |i32| -> ()) + //^ : (|i32| -> i32, |i32| -> ()) } "#, ); @@ -931,15 +899,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ {closure#0} + //^ : {closure#0} let y = |t: i32| x() + t; - //^ {closure#1} + //^ : {closure#1} let mut t = 5; - //^ i32 + //^ : i32 let z = |k: i32| { t += k; }; - //^ {closure#2} + //^ : {closure#2} let p = (y, z); - //^ ({closure#1}, {closure#2}) + //^ : ({closure#1}, {closure#2}) } "#, ); @@ -953,15 +921,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ … + //^ : … let y = |t: i32| x() + t; - //^ … + //^ : … let mut t = 5; - //^ i32 + //^ : i32 let z = |k: i32| { t += k; }; - //^ … + //^ : … let p = (y, z); - //^ (…, …) + //^ : (…, …) } "#, ); @@ -981,24 +949,24 @@ fn main() { let multiple_2 = |x: i32| { x * 2 }; let multiple_2 = |x: i32| x * 2; - // ^^^^^^^^^^ impl Fn(i32) -> i32 + // ^^^^^^^^^^ : impl Fn(i32) -> i32 let (not) = (|x: bool| { !x }); - // ^^^ impl Fn(bool) -> bool + // ^^^ : impl Fn(bool) -> bool let (is_zero, _b) = (|x: usize| { x == 0 }, false); - // ^^^^^^^ impl Fn(usize) -> bool - // ^^ bool + // ^^^^^^^ : impl Fn(usize) -> bool + // ^^ : bool let plus_one = |x| { x + 1 }; - // ^ u8 + // ^ : u8 foo(plus_one); let add_mul = bar(|x: u8| { x + 1 }); - // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized + // ^^^^^^^ : impl FnOnce(u8) -> u8 + ?Sized let closure = if let Some(6) = add_mul(2).checked_sub(1) { - // ^^^^^^^ fn(i32) -> i32 + // ^^^^^^^ : fn(i32) -> i32 |x: i32| { x * 2 } } else { |x: i32| { x * 3 } @@ -1025,11 +993,11 @@ struct VeryLongOuterName(T); fn main() { let a = Smol(0u32); - //^ Smol + //^ : Smol let b = VeryLongOuterName(0usize); - //^ VeryLongOuterName<…> + //^ : VeryLongOuterName<…> let c = Smol(Smol(0u32)) - //^ Smol> + //^ : Smol> }"#, ); } diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index dada4dd048537..343cf17e50e0c 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -7,7 +7,7 @@ use ide_db::RootDatabase; use syntax::ast::{self, AstNode}; -use crate::{InlayHint, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -54,6 +54,9 @@ pub(super) fn hints( kind: InlayKind::BindingMode, label: r.to_string().into(), text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: mut_reference, }); }); match pat { @@ -69,11 +72,20 @@ pub(super) fn hints( kind: InlayKind::BindingMode, label: bm.to_string().into(), text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: true, }); } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { - acc.push(InlayHint::opening_paren(pat.syntax().text_range())); - acc.push(InlayHint::closing_paren(pat.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::BindingMode, + pat.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after( + InlayKind::BindingMode, + pat.syntax().text_range(), + )); } _ => (), } diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index d8c8401af77d6..cd1ac1e55e8b2 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -5,7 +5,7 @@ use syntax::{ Direction, NodeOrToken, SyntaxKind, T, }; -use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; use super::label_of_ty; @@ -60,8 +60,11 @@ pub(super) fn hints( acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::Chaining, - label: label_of_ty(famous_defs, config, ty)?, + label: label_of_ty(famous_defs, config, &ty)?, text_edit: None, + position: InlayHintPosition::After, + pad_left: true, + pad_right: false, }); } } @@ -104,6 +107,9 @@ fn main() { [ InlayHint { range: 147..172, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -125,6 +131,9 @@ fn main() { }, InlayHint { range: 147..154, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -191,6 +200,9 @@ fn main() { [ InlayHint { range: 143..190, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -212,6 +224,9 @@ fn main() { }, InlayHint { range: 143..179, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -262,6 +277,9 @@ fn main() { [ InlayHint { range: 143..190, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -283,6 +301,9 @@ fn main() { }, InlayHint { range: 143..179, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -334,6 +355,9 @@ fn main() { [ InlayHint { range: 246..283, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -368,6 +392,9 @@ fn main() { }, InlayHint { range: 246..265, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -434,6 +461,9 @@ fn main() { [ InlayHint { range: 174..241, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -468,6 +498,9 @@ fn main() { }, InlayHint { range: 174..224, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -502,6 +535,9 @@ fn main() { }, InlayHint { range: 174..206, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -536,6 +572,9 @@ fn main() { }, InlayHint { range: 174..189, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "&mut ", @@ -586,9 +625,12 @@ fn main() { [ InlayHint { range: 124..130, + position: Before, + pad_left: true, + pad_right: false, kind: Type, label: [ - "", + ": ", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -616,6 +658,9 @@ fn main() { }, InlayHint { range: 145..185, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -637,6 +682,9 @@ fn main() { }, InlayHint { range: 145..168, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -658,6 +706,9 @@ fn main() { }, InlayHint { range: 222..228, + position: Before, + pad_left: false, + pad_right: true, kind: Parameter, label: [ InlayHintLabelPart { diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 10b5acd064ea7..cd5a815abb0c4 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -10,7 +10,7 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind}; +use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -113,6 +113,9 @@ pub(super) fn hints( kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), text_edit: None, + position: InlayHintPosition::After, + pad_left: true, + pad_right: false, }); None diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index 60c4fe411f4fa..3ee118f6e8c79 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode}; use text_edit::{TextRange, TextSize}; -use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -35,6 +35,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label: InlayHintLabel::simple("move", None, None), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); range } @@ -44,6 +47,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { @@ -71,6 +77,9 @@ pub(super) fn hints( source.name().and_then(|name| sema.original_range_opt(name.syntax())), ), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); if idx != last { @@ -79,6 +88,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label: InlayHintLabel::simple(", ", None, None), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); } } @@ -87,6 +99,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }); Some(()) diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 6214e9c8e7ff3..3b41db0f13d0a 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs @@ -6,7 +6,7 @@ use syntax::ast::{self, AstNode}; use crate::{ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, - ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, + ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, }; pub(super) fn hints( @@ -20,9 +20,12 @@ pub(super) fn hints( return None; } - if closure.ret_type().is_some() { - return None; - } + let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some())); + let arrow = match ret_type { + Some((_, true)) => return None, + Some((arrow, _)) => arrow, + None => None, + }; let has_block_body = closure_has_block_body(&closure); if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock { @@ -35,18 +38,26 @@ pub(super) fn hints( let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted(); let callable = ty.as_callable(sema.db)?; let ty = callable.return_type(); - if ty.is_unit() { + if arrow.is_none() && ty.is_unit() { return None; } + let mut label = label_of_ty(famous_defs, config, &ty)?; + + if arrow.is_none() { + label.prepend_str(" -> "); + } // FIXME?: We could provide text edit to insert braces for closures with non-block body. let text_edit = if has_block_body { ty_to_text_edit( sema, closure.syntax(), &ty, - param_list.syntax().text_range().end(), - String::from(" -> "), + arrow + .as_ref() + .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range()) + .end(), + if arrow.is_none() { String::from(" -> ") } else { String::new() }, ) } else { None @@ -54,9 +65,36 @@ pub(super) fn hints( acc.push(InlayHint { range: param_list.syntax().text_range(), - kind: InlayKind::ClosureReturnType, - label: label_of_ty(famous_defs, config, ty)?, + kind: InlayKind::Type, + label, text_edit, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); Some(()) } + +#[cfg(test)] +mod tests { + use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG}; + + use super::*; + + #[test] + fn return_type_hints_for_closure_without_block() { + check_with_config( + InlayHintsConfig { + closure_return_type_hints: ClosureReturnTypeHints::Always, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let a = || { 0 }; + //^^ -> i32 + let b = || 0; + //^^ -> i32 +}"#, + ); + } +} diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index f9047efaf1af5..9bff98f6110cb 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -9,7 +9,8 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; use syntax::ast::{self, AstNode, HasName}; use crate::{ - DiscriminantHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip, + DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + InlayTooltip, }; pub(super) fn enum_hints( @@ -41,10 +42,11 @@ fn variant_hints( sema: &Semantics<'_, RootDatabase>, variant: &ast::Variant, ) -> Option<()> { - if variant.eq_token().is_some() { + if variant.expr().is_some() { return None; } + let eq_token = variant.eq_token(); let name = variant.name()?; let descended = sema.descend_node_into_attributes(variant.clone()).pop(); @@ -52,30 +54,39 @@ fn variant_hints( let v = sema.to_def(desc_pat)?; let d = v.eval(sema.db); + let range = match variant.field_list() { + Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()), + None => name.syntax().text_range(), + }; + let eq_ = if eq_token.is_none() { " =" } else { "" }; + let label = InlayHintLabel::simple( + match d { + Ok(x) => { + if x >= 10 { + format!("{eq_} {x} ({x:#X})") + } else { + format!("{eq_} {x}") + } + } + Err(_) => format!("{eq_} ?"), + }, + Some(InlayTooltip::String(match &d { + Ok(_) => "enum variant discriminant".into(), + Err(e) => format!("{e:?}").into(), + })), + None, + ); acc.push(InlayHint { - range: match variant.field_list() { - Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()), - None => name.syntax().text_range(), + range: match eq_token { + Some(t) => range.cover(t.text_range()), + _ => range, }, kind: InlayKind::Discriminant, - label: InlayHintLabel::simple( - match d { - Ok(x) => { - if x >= 10 { - format!("{x} ({x:#X})") - } else { - format!("{x}") - } - } - Err(_) => "?".into(), - }, - Some(InlayTooltip::String(match &d { - Ok(_) => "enum variant discriminant".into(), - Err(e) => format!("{e:?}").into(), - })), - None, - ), + label, text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); Some(()) @@ -113,14 +124,14 @@ mod tests { r#" enum Enum { Variant, -//^^^^^^^0 +//^^^^^^^ = 0$ Variant1, -//^^^^^^^^1 +//^^^^^^^^ = 1$ Variant2, -//^^^^^^^^2 +//^^^^^^^^ = 2$ Variant5 = 5, Variant6, -//^^^^^^^^6 +//^^^^^^^^ = 6$ } "#, ); @@ -128,14 +139,14 @@ enum Enum { r#" enum Enum { Variant, -//^^^^^^^0 +//^^^^^^^ = 0 Variant1, -//^^^^^^^^1 +//^^^^^^^^ = 1 Variant2, -//^^^^^^^^2 +//^^^^^^^^ = 2 Variant5 = 5, Variant6, -//^^^^^^^^6 +//^^^^^^^^ = 6 } "#, ); @@ -147,16 +158,16 @@ enum Enum { r#" enum Enum { Variant(), - //^^^^^^^^^0 + //^^^^^^^^^ = 0 Variant1, - //^^^^^^^^1 + //^^^^^^^^ = 1 Variant2 {}, - //^^^^^^^^^^^2 + //^^^^^^^^^^^ = 2 Variant3, - //^^^^^^^^3 + //^^^^^^^^ = 3 Variant5 = 5, Variant6, - //^^^^^^^^6 + //^^^^^^^^ = 6 } "#, ); @@ -180,16 +191,16 @@ enum Enum { r#" enum Enum { Variant(), - //^^^^^^^^^0 + //^^^^^^^^^ = 0 Variant1, - //^^^^^^^^1 + //^^^^^^^^ = 1 Variant2 {}, - //^^^^^^^^^^^2 + //^^^^^^^^^^^ = 2 Variant3, - //^^^^^^^^3 + //^^^^^^^^ = 3 Variant5 = 5, Variant6, - //^^^^^^^^6 + //^^^^^^^^ = 6 } "#, ); diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 34eb5eb94c4d0..5fce11b785a7a 100644 --- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -10,7 +10,7 @@ use syntax::{ SyntaxToken, }; -use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; pub(super) fn hints( acc: &mut Vec, @@ -26,6 +26,9 @@ pub(super) fn hints( kind: InlayKind::Lifetime, label: label.into(), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }; let param_list = func.param_list()?; @@ -191,6 +194,9 @@ pub(super) fn hints( ) .into(), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }); } (None, allocated_lifetimes) => acc.push(InlayHint { @@ -198,6 +204,9 @@ pub(super) fn hints( kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }), } Some(()) diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index ba875649f7993..fc297a8d824f2 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/crates/ide/src/inlay_hints/implicit_static.rs @@ -8,7 +8,7 @@ use syntax::{ SyntaxKind, }; -use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; pub(super) fn hints( acc: &mut Vec, @@ -35,6 +35,9 @@ pub(super) fn hints( kind: InlayKind::Lifetime, label: "'static".to_owned().into(), text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }); } } diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 9729a43c220aa..c4f43f411753e 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -10,7 +10,7 @@ use ide_db::{base_db::FileRange, RootDatabase}; use stdx::to_lower_snake_case; use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp}; -use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -31,16 +31,16 @@ pub(super) fn hints( // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; let (param_name, name_syntax) = match param.as_ref()? { - Either::Left(pat) => ("self".to_string(), pat.name()), + Either::Left(pat) => (pat.name()?, pat.name()), Either::Right(pat) => match pat { - ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()), + ast::Pat::IdentPat(it) => (it.name()?, it.name()), _ => return None, }, }; Some((name_syntax, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, param_name, arg) + !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) }) .map(|(param, param_name, _, FileRange { range, .. })| { let mut linked_location = None; @@ -53,11 +53,17 @@ pub(super) fn hints( } } + let colon = if config.render_colons { ":" } else { "" }; + let label = + InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { range, kind: InlayKind::Parameter, - label: InlayHintLabel::simple(param_name, None, linked_location), + label, text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: true, } }); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b84d5df810776..72d20af663756 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -87,8 +87,8 @@ pub use crate::{ hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayHintsConfig, InlayKind, InlayTooltip, - LifetimeElisionHints, + InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, + InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 9e9dfaaf5a37d..96629b90ef59a 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1349,9 +1349,7 @@ pub(crate) fn handle_inlay_hints( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() - .map(|it| { - to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) - }) + .map(|it| to_proto::inlay_hint(&snap, &line_index, it)) .collect::>>()?, )) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1b7fd558906cd..521b7a76279c6 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -434,87 +434,21 @@ pub(crate) fn signature_help( pub(crate) fn inlay_hint( snap: &GlobalStateSnapshot, line_index: &LineIndex, - render_colons: bool, - mut inlay_hint: InlayHint, + inlay_hint: InlayHint, ) -> Cancellable { - match inlay_hint.kind { - InlayKind::Parameter if render_colons => inlay_hint.label.append_str(":"), - InlayKind::Type if render_colons => inlay_hint.label.prepend_str(": "), - InlayKind::ClosureReturnType => inlay_hint.label.prepend_str(" -> "), - InlayKind::Discriminant => inlay_hint.label.prepend_str(" = "), - _ => {} - } - let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?; Ok(lsp_types::InlayHint { - position: match inlay_hint.kind { - // before annotated thing - InlayKind::OpeningParenthesis - | InlayKind::Parameter - | InlayKind::Adjustment - | InlayKind::BindingMode => position(line_index, inlay_hint.range.start()), - // after annotated thing - InlayKind::ClosureReturnType - | InlayKind::ClosureCapture - | InlayKind::Type - | InlayKind::Discriminant - | InlayKind::Chaining - | InlayKind::GenericParamList - | InlayKind::ClosingParenthesis - | InlayKind::AdjustmentPostfix - | InlayKind::Lifetime - | InlayKind::ClosingBrace => position(line_index, inlay_hint.range.end()), + position: match inlay_hint.position { + ide::InlayHintPosition::Before => position(line_index, inlay_hint.range.start()), + ide::InlayHintPosition::After => position(line_index, inlay_hint.range.end()), }, - padding_left: Some(match inlay_hint.kind { - InlayKind::Type => !render_colons, - InlayKind::Chaining | InlayKind::ClosingBrace => true, - InlayKind::ClosingParenthesis - | InlayKind::ClosureCapture - | InlayKind::Discriminant - | InlayKind::OpeningParenthesis - | InlayKind::BindingMode - | InlayKind::ClosureReturnType - | InlayKind::GenericParamList - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::Lifetime - | InlayKind::Parameter => false, - }), - padding_right: Some(match inlay_hint.kind { - InlayKind::ClosingParenthesis - | InlayKind::OpeningParenthesis - | InlayKind::Chaining - | InlayKind::ClosureReturnType - | InlayKind::GenericParamList - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::Type - | InlayKind::Discriminant - | InlayKind::ClosingBrace => false, - InlayKind::ClosureCapture => { - matches!(&label, lsp_types::InlayHintLabel::String(s) if s == ")") - } - InlayKind::BindingMode => { - matches!(&label, lsp_types::InlayHintLabel::String(s) if s != "&") - } - InlayKind::Parameter | InlayKind::Lifetime => true, - }), + padding_left: Some(inlay_hint.pad_left), + padding_right: Some(inlay_hint.pad_right), kind: match inlay_hint.kind { InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER), - InlayKind::ClosureReturnType | InlayKind::Type | InlayKind::Chaining => { - Some(lsp_types::InlayHintKind::TYPE) - } - InlayKind::ClosingParenthesis - | InlayKind::ClosureCapture - | InlayKind::Discriminant - | InlayKind::OpeningParenthesis - | InlayKind::BindingMode - | InlayKind::GenericParamList - | InlayKind::Lifetime - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::ClosingBrace => None, + InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE), + _ => None, }, text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)), data: None, From edd60f7b0d02e85a9c0ee60620cb3347b37e5fdf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 11:02:57 +0200 Subject: [PATCH 400/806] Simplify bind pat filtering --- crates/ide/src/inlay_hints/bind_pat.rs | 103 ++++++++----------------- 1 file changed, 32 insertions(+), 71 deletions(-) diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index f98cc129a9386..3d5122a7cf3e7 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -3,7 +3,7 @@ //! fn f(a: i32, b: i32) -> i32 { a + b } //! let _x /* i32 */= f(4, 4); //! ``` -use hir::{Semantics, TypeInfo}; +use hir::Semantics; use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; @@ -28,11 +28,41 @@ pub(super) fn hints( return None; } + let parent = pat.syntax().parent()?; + let type_ascriptable = match_ast! { + match parent { + ast::Param(it) => { + if it.ty().is_some() { + return None; + } + Some(it.colon_token()) + }, + ast::LetStmt(it) => { + if config.hide_closure_initialization_hints { + if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { + if closure_has_block_body(&closure) { + return None; + } + } + } + if it.ty().is_some() { + return None; + } + Some(it.colon_token()) + }, + _ => None + } + }; + let descended = sema.descend_node_into_attributes(pat.clone()).pop(); let desc_pat = descended.as_ref().unwrap_or(pat); let ty = sema.type_of_binding_in_pat(desc_pat)?; - if should_not_display_type_hint(sema, config, pat, &ty) { + if ty.is_unknown() { + return None; + } + + if sema.resolve_bind_pat_to_const(pat).is_some() { return None; } @@ -44,11 +74,6 @@ pub(super) fn hints( return None; } - let type_ascriptable = desc_pat.syntax().parent().and_then(|it| { - ast::LetStmt::cast(it.clone()) - .map(|it| it.colon_token()) - .or_else(|| ast::Param::cast(it).map(|it| it.colon_token())) - }); let text_edit = if let Some(colon_token) = &type_ascriptable { ty_to_text_edit( sema, @@ -89,57 +114,6 @@ pub(super) fn hints( Some(()) } -fn should_not_display_type_hint( - sema: &Semantics<'_, RootDatabase>, - config: &InlayHintsConfig, - bind_pat: &ast::IdentPat, - pat_ty: &hir::Type, -) -> bool { - let db = sema.db; - - if pat_ty.is_unknown() { - return true; - } - - if sema.resolve_bind_pat_to_const(bind_pat).is_some() { - return true; - } - - for node in bind_pat.syntax().ancestors() { - match_ast! { - match node { - ast::LetStmt(it) => { - if config.hide_closure_initialization_hints { - if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { - if closure_has_block_body(&closure) { - return true; - } - } - } - return it.ty().is_some() - }, - // FIXME: We might wanna show type hints in parameters for non-top level patterns as well - ast::Param(it) => return it.ty().is_some(), - ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), - ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), - ast::IfExpr(_) => return false, - ast::WhileExpr(_) => return false, - ast::ForExpr(it) => { - // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit). - // Type of expr should be iterable. - return it.in_token().is_none() || - it.iterable() - .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr)) - .map(TypeInfo::original) - .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) - }, - _ => (), - } - } - } - false -} - fn is_named_constructor( sema: &Semantics<'_, RootDatabase>, pat: &ast::IdentPat, @@ -193,19 +167,6 @@ fn is_named_constructor( (ctor_name == ty_name).then_some(()) } -fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool { - if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() { - let pat_text = bind_pat.to_string(); - enum_data - .variants(db) - .into_iter() - .map(|variant| variant.name(db).to_smol_str()) - .any(|enum_name| enum_name == pat_text) - } else { - false - } -} - #[cfg(test)] mod tests { // This module also contains tests for super::closure_ret From 8e116855f5326ea5bda7181857400c2e27831259 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 11:43:39 +0200 Subject: [PATCH 401/806] Add macro modifier for highlighting tokens in macro calls --- crates/ide/src/syntax_highlighting.rs | 78 +++++++------ crates/ide/src/syntax_highlighting/inject.rs | 29 ++++- crates/ide/src/syntax_highlighting/tags.rs | 8 +- .../test_data/highlight_doctest.html | 6 +- .../test_data/highlight_general.html | 6 +- .../test_data/highlight_injection.html | 24 ++-- .../test_data/highlight_keywords.html | 2 +- .../test_data/highlight_macros.html | 28 ++--- .../test_data/highlight_strings.html | 110 +++++++++--------- .../test_data/highlight_unsafe.html | 10 +- crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 1 + 12 files changed, 172 insertions(+), 131 deletions(-) diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 6bfc71f939762..751e51da0d289 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -166,6 +166,7 @@ pub struct HighlightConfig { // injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. // intraDocLink:: Emitted for intra doc links in doc-strings. // library:: Emitted for items that are defined outside of the current crate. +// macro:: Emitted for tokens inside macro calls. // mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. // public:: Emitted for items that are from the current crate and are `pub`. // reference:: Emitted for locals behind a reference and functions taking `self` by reference. @@ -240,6 +241,7 @@ fn traverse( let mut current_macro: Option = None; let mut macro_highlighter = MacroHighlighter::default(); let mut inside_attribute = false; + let mut inside_macro_call = false; // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -270,46 +272,50 @@ fn traverse( inside_attribute = false } - Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { - match ast::Item::cast(node.clone()) { - Some(ast::Item::MacroRules(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(ast::Item::MacroDef(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(item) => { - if matches!(node.kind(), FN | CONST | STATIC) { - bindings_shadow_count.clear(); + Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) { + Some(item) => { + match item { + ast::Item::MacroRules(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; + } + ast::Item::MacroDef(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; } + ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { + bindings_shadow_count.clear() + } + ast::Item::MacroCall(_) => { + inside_macro_call = true; + } + _ => (), + } - if attr_or_derive_item.is_none() { - if sema.is_attr_macro_call(&item) { - attr_or_derive_item = Some(AttrOrDerive::Attr(item)); - } else { - let adt = match item { - ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), - ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), - ast::Item::Union(it) => Some(ast::Adt::Union(it)), - _ => None, - }; - match adt { - Some(adt) if sema.is_derive_annotated(&adt) => { - attr_or_derive_item = - Some(AttrOrDerive::Derive(ast::Item::from(adt))); - } - _ => (), + if attr_or_derive_item.is_none() { + if sema.is_attr_macro_call(&item) { + attr_or_derive_item = Some(AttrOrDerive::Attr(item)); + } else { + let adt = match item { + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }; + match adt { + Some(adt) if sema.is_derive_annotated(&adt) => { + attr_or_derive_item = + Some(AttrOrDerive::Derive(ast::Item::from(adt))); } + _ => (), } } } - _ => (), } - } + _ => (), + }, Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { match ast::Item::cast(node.clone()) { Some(ast::Item::MacroRules(mac)) => { @@ -327,6 +333,9 @@ fn traverse( { attr_or_derive_item = None; } + Some(ast::Item::MacroCall(_)) => { + inside_macro_call = false; + } _ => (), } } @@ -476,6 +485,9 @@ fn traverse( if inside_attribute { highlight |= HlMod::Attribute } + if inside_macro_call && tt_level > 0 { + highlight |= HlMod::Macro + } hl.add(HlRange { range, highlight, binding_hash }); } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 3c4cfc78152dd..901df147d32c0 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -52,7 +52,11 @@ pub(super) fn ra_fixture( if let Some(next) = text.strip_prefix(marker) { if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) { - hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None }); + hl.add(HlRange { + range, + highlight: HlTag::Keyword | HlMod::Injected, + binding_hash: None, + }); } text = next; @@ -66,7 +70,16 @@ pub(super) fn ra_fixture( for mut hl_range in analysis .highlight( - HighlightConfig { syntactic_name_ref_highlighting: false, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: false, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, ) .unwrap() @@ -74,6 +87,7 @@ pub(super) fn ra_fixture( for range in inj.map_range_up(hl_range.range) { if let Some(range) = literal.map_range_up(range) { hl_range.range = range; + hl_range.highlight |= HlMod::Injected; hl.add(hl_range); } } @@ -217,7 +231,16 @@ pub(super) fn doc_comment( if let Ok(ranges) = analysis.with_db(|db| { super::highlight( db, - HighlightConfig { syntactic_name_ref_highlighting: true, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: true, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, None, ) diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index a81c4ee0cbd41..f983109115f6c 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -49,7 +49,7 @@ pub enum HlMod { Associated = 0, /// Used with keywords like `async` and `await`. Async, - /// Used to differentiate individual elements within attributes. + /// Used to differentiate individual elements within attribute calls. Attribute, /// Callable item or value. Callable, @@ -72,6 +72,8 @@ pub enum HlMod { IntraDocLink, /// Used for items from other crates. Library, + /// Used to differentiate individual elements within macro calls. + Macro, /// Mutable binding. Mutable, /// Used for public items. @@ -200,7 +202,7 @@ impl fmt::Display for HlTag { } impl HlMod { - const ALL: &'static [HlMod; 19] = &[ + const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[ HlMod::Associated, HlMod::Async, HlMod::Attribute, @@ -214,6 +216,7 @@ impl HlMod { HlMod::Injected, HlMod::IntraDocLink, HlMod::Library, + HlMod::Macro, HlMod::Mutable, HlMod::Public, HlMod::Reference, @@ -237,6 +240,7 @@ impl HlMod { HlMod::Injected => "injected", HlMod::IntraDocLink => "intra_doc_link", HlMod::Library => "library", + HlMod::Macro => "macro", HlMod::Mutable => "mutable", HlMod::Public => "public", HlMod::Reference => "reference", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 18045f1f55afd..35f240d428471 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -93,7 +93,7 @@ /// let foo = Foo::new(); /// /// // calls bar on foo - /// assert!(foo.bar()); + /// assert!(foo.bar()); /// /// let bar = foo.bar || Foo::bar; /// @@ -145,7 +145,7 @@ /// ``` /// macro_rules! noop { ($expr:expr) => { $expr }} -/// noop!(1); +/// noop!(1); /// ``` macro_rules! noop { ($expr:expr) => { @@ -165,7 +165,7 @@ /// #[cfg_attr(feature = "alloc", doc = "```rust")] #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] -/// let _ = example(&alloc::vec![1, 2, 3]); +/// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 9f2b1926b511d..6b049f379ac1d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -178,7 +178,7 @@ impl<T> Option<T> { fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { - None => unimplemented!(), + None => unimplemented!(), Nope => Nope, } } @@ -192,7 +192,7 @@ async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); - futures::join!(f1, f2); + futures::join!(f1, f2); } fn use_foo_items() { @@ -204,7 +204,7 @@ let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index abcd80c280bf3..d9c3db6fbb510 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -45,18 +45,18 @@
fn fixture(ra_fixture: &str) {}
 
 fn main() {
-    fixture(r#"
-trait Foo {
-    fn foo() {
-        println!("2 + 2 = {}", 4);
-    }
-}"#
+    fixture(r#"
+trait Foo {
+    fn foo() {
+        println!("2 + 2 = {}", 4);
+    }
+}"#
     );
-    fixture(r"
-fn foo() {
-    foo($0{
-        92
-    }$0)
-}"
+    fixture(r"
+fn foo() {
+    foo($0{
+        92
+    }$0)
+}"
     );
 }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 66f9ede96295f..3900959bedf8d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -53,6 +53,6 @@ macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); +void!(Self); struct __ where Self:; fn __(_: Self) {} \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 54d4279525dd5..2cbbf69641525 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -42,21 +42,21 @@ .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
proc_macros::mirror! {
-    {
-        ,i32 :x pub
-        ,i32 :y pub
-    } Foo struct
-}
+
proc_macros::mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
+}
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
 
-def_fn! {
-    fn bar() -> u32 {
-        100
-    }
-}
+def_fn! {
+    fn bar() -> u32 {
+        100
+    }
+}
 
 macro_rules! dont_color_me_braces {
     () => {0}
@@ -90,7 +90,7 @@
 }
 
 fn main() {
-    println!("Hello, {}!", 92);
-    dont_color_me_braces!();
-    noop!(noop!(1));
+    println!("Hello, {}!", 92);
+    dont_color_me_braces!();
+    noop!(noop!(1));
 }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 4c6d69bc63268..6acc62e0f1eb4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -103,73 +103,73 @@ let a = '\x65'; let a = '\x00'; - println!("Hello {{Hello}}"); + println!("Hello {{Hello}}"); // from https://doc.rust-lang.org/std/fmt/index.html - println!("Hello"); // => "Hello" - println!("Hello, {}!", "world"); // => "Hello, world!" - println!("The number is {}", 1); // => "The number is 1" - println!("{:?}", (3, 4)); // => "(3, 4)" - println!("{value}", value=4); // => "4" - println!("{} {}", 1, 2); // => "1 2" - println!("{:04}", 42); // => "0042" with leading zerosV - println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" - println!("{argument}", argument = "test"); // => "test" - println!("{name} {}", 1, name = 2); // => "2 1" - println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" - println!("{{{}}}", 2); // => "{2}" - println!("Hello {:5}!", "x"); - println!("Hello {:1$}!", "x", 5); - println!("Hello {1:0$}!", 5, "x"); - println!("Hello {:width$}!", "x", width = 5); - println!("Hello {:<5}!", "x"); - println!("Hello {:-<5}!", "x"); - println!("Hello {:^5}!", "x"); - println!("Hello {:>5}!", "x"); - println!("Hello {:+}!", 5); - println!("{:#x}!", 27); - println!("Hello {:05}!", 5); - println!("Hello {:05}!", -5); - println!("{:#010x}!", 27); - println!("Hello {0} is {1:.5}", "x", 0.01); - println!("Hello {1} is {2:.0$}", 5, "x", 0.01); - println!("Hello {0} is {2:.1$}", "x", 5, 0.01); - println!("Hello {} is {:.*}", "x", 5, 0.01); - println!("Hello {} is {2:.*}", "x", 5, 0.01); - println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); - println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); - println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); - println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); + println!("Hello"); // => "Hello" + println!("Hello, {}!", "world"); // => "Hello, world!" + println!("The number is {}", 1); // => "The number is 1" + println!("{:?}", (3, 4)); // => "(3, 4)" + println!("{value}", value=4); // => "4" + println!("{} {}", 1, 2); // => "1 2" + println!("{:04}", 42); // => "0042" with leading zerosV + println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" + println!("{argument}", argument = "test"); // => "test" + println!("{name} {}", 1, name = 2); // => "2 1" + println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" + println!("{{{}}}", 2); // => "{2}" + println!("Hello {:5}!", "x"); + println!("Hello {:1$}!", "x", 5); + println!("Hello {1:0$}!", 5, "x"); + println!("Hello {:width$}!", "x", width = 5); + println!("Hello {:<5}!", "x"); + println!("Hello {:-<5}!", "x"); + println!("Hello {:^5}!", "x"); + println!("Hello {:>5}!", "x"); + println!("Hello {:+}!", 5); + println!("{:#x}!", 27); + println!("Hello {:05}!", 5); + println!("Hello {:05}!", -5); + println!("{:#010x}!", 27); + println!("Hello {0} is {1:.5}", "x", 0.01); + println!("Hello {1} is {2:.0$}", 5, "x", 0.01); + println!("Hello {0} is {2:.1$}", "x", 5, 0.01); + println!("Hello {} is {:.*}", "x", 5, 0.01); + println!("Hello {} is {2:.*}", "x", 5, 0.01); + println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); + println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); + println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); + println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); let _ = "{}" let _ = "{{}}"; - println!("Hello {{}}"); - println!("{{ Hello"); - println!("Hello }}"); - println!("{{Hello}}"); - println!("{{ Hello }}"); - println!("{{Hello }}"); - println!("{{ Hello}}"); + println!("Hello {{}}"); + println!("{{ Hello"); + println!("Hello }}"); + println!("{{Hello}}"); + println!("{{ Hello }}"); + println!("{{Hello }}"); + println!("{{ Hello}}"); - println!(r"Hello, {}!", "world"); + println!(r"Hello, {}!", "world"); // escape sequences - println!("Hello\nWorld"); - println!("\u{48}\x65\x6C\x6C\x6F World"); + println!("Hello\nWorld"); + println!("\u{48}\x65\x6C\x6C\x6F World"); let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; let _ = r"\\"; - println!("{\x41}", A = 92); - println!("{ничоси}", ничоси = 92); + println!("{\x41}", A = 92); + println!("{ничоси}", ничоси = 92); - println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); - panic!("more {}", 1); - assert!(true, "{}", 1); - assert!(true, "{} asdasd", 1); - toho!("{}fmt", 0); - asm!("mov eax, {0}"); - format_args!(concat!("{}"), "{}"); + println!("{:x?} {} ", thingy, n2); + panic!("{}", 0); + panic!("more {}", 1); + assert!(true, "{}", 1); + assert!(true, "{} asdasd", 1); + toho!("{}fmt", 0); + asm!("mov eax, {0}"); + format_args!(concat!("{}"), "{}"); }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 1992bdc6ae35f..654d51b8a43d9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -89,13 +89,13 @@ let x = &5 as *const _ as *const usize; let u = Union { b: 0 }; - id! { - unsafe { unsafe_deref!() } - }; + id! { + unsafe { unsafe_deref!() } + }; unsafe { - unsafe_deref!(); - id! { unsafe_deref!() }; + unsafe_deref!(); + id! { unsafe_deref!() }; // unsafe fn and method calls unsafe_fn(); diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index e5b43c5a10c33..d4bb20c8f448e 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -141,6 +141,7 @@ define_semantic_token_modifiers![ (INJECTED, "injected"), (INTRA_DOC_LINK, "intraDocLink"), (LIBRARY, "library"), + (MACRO_MODIFIER, "macro"), (MUTABLE, "mutable"), (PUBLIC, "public"), (REFERENCE, "reference"), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 616bdddd92bb0..3428f7eba30a6 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -746,6 +746,7 @@ fn semantic_token_type_and_modifiers( HlMod::Injected => semantic_tokens::INJECTED, HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, HlMod::Library => semantic_tokens::LIBRARY, + HlMod::Macro => semantic_tokens::MACRO_MODIFIER, HlMod::Mutable => semantic_tokens::MUTABLE, HlMod::Public => semantic_tokens::PUBLIC, HlMod::Reference => semantic_tokens::REFERENCE, From f47caa666e0a2f7dd9ee9ac2535a2d26b301d4fb Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 11:51:28 +0200 Subject: [PATCH 402/806] Add AbsPath::absolutize --- crates/paths/src/lib.rs | 5 +++++ crates/project-model/src/project_json.rs | 15 ++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 0cfbf1d9a1687..e0c20a4143bd6 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -140,6 +140,11 @@ impl AbsPath { self.0.parent().map(AbsPath::assert) } + /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { + self.join(path).normalize() + } + /// Equivalent of [`Path::join`] for `AbsPath`. pub fn join(&self, path: impl AsRef) -> AbsPathBuf { self.as_ref().join(path).try_into().unwrap() diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index ede2dc769e3cb..80897f7478cf9 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -98,24 +98,23 @@ impl ProjectJson { /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via /// configuration. pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { - let absolutize = - |p| AbsPathBuf::try_from(p).unwrap_or_else(|path| base.join(&path)).normalize(); + let absolutize_on_base = |p| base.absolutize(p); ProjectJson { - sysroot: data.sysroot.map(absolutize), - sysroot_src: data.sysroot_src.map(absolutize), + sysroot: data.sysroot.map(absolutize_on_base), + sysroot_src: data.sysroot_src.map(absolutize_on_base), project_root: base.to_path_buf(), crates: data .crates .into_iter() .map(|crate_data| { - let root_module = absolutize(crate_data.root_module); + let root_module = absolutize_on_base(crate_data.root_module); let is_workspace_member = crate_data .is_workspace_member .unwrap_or_else(|| root_module.starts_with(base)); let (include, exclude) = match crate_data.source { Some(src) => { let absolutize = |dirs: Vec| { - dirs.into_iter().map(absolutize).collect::>() + dirs.into_iter().map(absolutize_on_base).collect::>() }; (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) } @@ -142,7 +141,9 @@ impl ProjectJson { cfg: crate_data.cfg, target: crate_data.target, env: crate_data.env, - proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(absolutize), + proc_macro_dylib_path: crate_data + .proc_macro_dylib_path + .map(absolutize_on_base), is_workspace_member, include, exclude, From 2e03b198cacf177a47402254b9ab702e1de50b11 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 17:41:09 +0200 Subject: [PATCH 403/806] fix: Fix perf regression from symbol index refactor --- crates/hir/src/symbols.rs | 99 +++++- .../test_symbol_index_collection.txt | 314 ++++++++++++++++++ crates/ide/src/navigation_target.rs | 34 +- 3 files changed, 434 insertions(+), 13 deletions(-) diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 386758ccab4c5..5d68aa52e62c7 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -1,12 +1,15 @@ //! File symbol extraction. +use base_db::FileRange; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, MacroId, ModuleDefId, ModuleId, TraitId, + src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, }; +use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; -use syntax::SmolStr; +use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; -use crate::{Module, ModuleDef}; +use crate::{Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -15,6 +18,45 @@ pub struct FileSymbol { // even though name can be derived from the def, we store it for efficiency pub name: SmolStr, pub def: ModuleDef, + pub loc: DeclarationLocation, + pub container_name: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DeclarationLocation { + /// The file id for both the `ptr` and `name_ptr`. + pub hir_file_id: HirFileId, + /// This points to the whole syntax node of the declaration. + pub ptr: SyntaxNodePtr, + /// This points to the [`syntax::ast::Name`] identifier of the declaration. + pub name_ptr: SyntaxNodePtr, +} + +impl DeclarationLocation { + pub fn syntax(&self, sema: &Semantics<'_, DB>) -> SyntaxNode { + let root = sema.parse_or_expand(self.hir_file_id); + self.ptr.to_node(&root) + } + + pub fn original_range(&self, db: &dyn HirDatabase) -> FileRange { + let node = resolve_node(db, self.hir_file_id, &self.ptr); + node.as_ref().original_file_range(db.upcast()) + } + + pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { + let node = resolve_node(db, self.hir_file_id, &self.name_ptr); + node.as_ref().original_file_range_opt(db.upcast()) + } +} + +fn resolve_node( + db: &dyn HirDatabase, + file_id: HirFileId, + ptr: &SyntaxNodePtr, +) -> InFile { + let root = db.parse_or_expand(file_id); + let node = ptr.to_node(&root); + InFile::new(file_id, node) } /// Represents an outstanding module that the symbol collector must collect symbols from. @@ -193,17 +235,52 @@ impl<'a> SymbolCollector<'a> { } } - fn push_decl(&mut self, id: impl Into) { - let def = ModuleDef::from(id.into()); - if let Some(name) = def.name(self.db) { - self.symbols.push(FileSymbol { name: name.to_smol_str(), def }); - } + fn push_decl(&mut self, id: L) + where + L: Lookup + Into, + ::Data: HasSource, + <::Data as HasSource>::Value: HasName, + { + self.push_file_symbol(|s| { + let loc = id.lookup(s.db.upcast()); + let source = loc.source(s.db.upcast()); + let name_node = source.value.name()?; + Some(FileSymbol { + name: name_node.text().into(), + def: ModuleDef::from(id.into()), + container_name: s.current_container_name.clone(), + loc: DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }, + }) + }) } fn push_module(&mut self, module_id: ModuleId) { - let def = Module::from(module_id); - if let Some(name) = def.name(self.db) { - self.symbols.push(FileSymbol { name: name.to_smol_str(), def: ModuleDef::Module(def) }); + self.push_file_symbol(|s| { + let def_map = module_id.def_map(s.db.upcast()); + let module_data = &def_map[module_id.local_id]; + let declaration = module_data.origin.declaration()?; + let module = declaration.to_node(s.db.upcast()); + let name_node = module.name()?; + Some(FileSymbol { + name: name_node.text().into(), + def: ModuleDef::Module(module_id.into()), + container_name: s.current_container_name.clone(), + loc: DeclarationLocation { + hir_file_id: declaration.file_id, + ptr: SyntaxNodePtr::new(module.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }, + }) + }) + } + + fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { + if let Some(file_symbol) = f(self) { + self.symbols.push(file_symbol); } } } diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1223e8d6b69ed..1e34dd633c84c 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -17,6 +17,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: TYPE_ALIAS, + range: 397..417, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 402..407, + }, + }, + container_name: None, }, FileSymbol { name: "CONST", @@ -27,6 +41,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: CONST, + range: 340..361, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 346..351, + }, + }, + container_name: None, }, FileSymbol { name: "CONST_WITH_INNER", @@ -37,6 +65,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: CONST, + range: 520..592, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 526..542, + }, + }, + container_name: None, }, FileSymbol { name: "Enum", @@ -49,6 +91,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: ENUM, + range: 185..207, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + }, + container_name: None, }, FileSymbol { name: "Macro", @@ -61,6 +117,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: MACRO_DEF, + range: 153..168, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + }, + container_name: None, }, FileSymbol { name: "STATIC", @@ -71,6 +141,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STATIC, + range: 362..396, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 369..375, + }, + }, + container_name: None, }, FileSymbol { name: "Struct", @@ -83,6 +167,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 170..184, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + }, + container_name: None, }, FileSymbol { name: "StructFromMacro", @@ -95,6 +193,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 2147483648, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..22, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + }, + container_name: None, }, FileSymbol { name: "StructInFn", @@ -107,6 +219,22 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 318..336, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 325..335, + }, + }, + container_name: Some( + "main", + ), }, FileSymbol { name: "StructInNamedConst", @@ -119,6 +247,22 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 555..581, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 562..580, + }, + }, + container_name: Some( + "CONST_WITH_INNER", + ), }, FileSymbol { name: "StructInUnnamedConst", @@ -131,6 +275,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 479..507, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 486..506, + }, + }, + container_name: None, }, FileSymbol { name: "Trait", @@ -141,6 +299,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: TRAIT, + range: 261..300, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 267..272, + }, + }, + container_name: None, }, FileSymbol { name: "Union", @@ -153,6 +325,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: UNION, + range: 208..222, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + }, + container_name: None, }, FileSymbol { name: "a_mod", @@ -165,6 +351,20 @@ }, }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: MODULE, + range: 419..457, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 423..428, + }, + }, + container_name: None, }, FileSymbol { name: "b_mod", @@ -177,6 +377,20 @@ }, }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: MODULE, + range: 594..604, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 598..603, + }, + }, + container_name: None, }, FileSymbol { name: "define_struct", @@ -189,6 +403,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: MACRO_RULES, + range: 51..131, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + }, + container_name: None, }, FileSymbol { name: "impl_fn", @@ -199,6 +427,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: FN, + range: 242..257, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + }, + container_name: None, }, FileSymbol { name: "macro_rules_macro", @@ -211,6 +453,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: MACRO_RULES, + range: 1..48, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + }, + container_name: None, }, FileSymbol { name: "main", @@ -221,6 +477,20 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: FN, + range: 302..338, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 305..309, + }, + }, + container_name: None, }, FileSymbol { name: "trait_fn", @@ -231,6 +501,22 @@ ), }, ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: FN, + range: 279..298, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 282..290, + }, + }, + container_name: Some( + "Trait", + ), }, ], ), @@ -254,6 +540,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 435..455, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 442..454, + }, + }, + container_name: None, }, ], ), @@ -277,6 +577,20 @@ }, ), ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 1, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..20, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + }, + container_name: None, }, ], ), diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index b5e410eaeb9e8..d8ce79de3755e 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -4,8 +4,8 @@ use std::fmt; use either::Either; use hir::{ - AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, HirDisplay, InFile, - LocalSource, ModuleSource, + symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, + HirDisplay, InFile, LocalSource, ModuleSource, }; use ide_db::{ base_db::{FileId, FileRange}, @@ -158,6 +158,36 @@ impl NavigationTarget { } } +impl TryToNav for FileSymbol { + fn try_to_nav(&self, db: &RootDatabase) -> Option { + let full_range = self.loc.original_range(db); + let name_range = self.loc.original_name_range(db)?; + + Some(NavigationTarget { + file_id: full_range.file_id, + name: self.name.clone(), + kind: Some(hir::ModuleDefId::from(self.def).into()), + full_range: full_range.range, + focus_range: Some(name_range.range), + container_name: self.container_name.clone(), + description: match self.def { + hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), + hir::ModuleDef::BuiltinType(_) => None, + }, + docs: None, + }) + } +} + impl TryToNav for Definition { fn try_to_nav(&self, db: &RootDatabase) -> Option { match self { From 9fff960636afa0d3edc01891265341c6e4c0cd14 Mon Sep 17 00:00:00 2001 From: mataha Date: Sat, 13 May 2023 18:38:12 +0200 Subject: [PATCH 404/806] Remove root from patched UNC windows path drives --- crates/rust-analyzer/src/bin/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index b255a4405387a..992e174a421f5 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -246,7 +246,7 @@ fn patch_path_prefix(path: PathBuf) -> PathBuf { format!("{}:", d.to_ascii_uppercase() as char) } Prefix::VerbatimDisk(d) => { - format!(r"\\?\{}:\", d.to_ascii_uppercase() as char) + format!(r"\\?\{}:", d.to_ascii_uppercase() as char) } _ => return path, }; From e9ddb62c658b35e3026c470217f38a465f2a3dc7 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 14 May 2023 00:50:51 +0900 Subject: [PATCH 405/806] Expand more single ident macro calls upon their collection --- crates/hir-def/src/nameres/collector.rs | 37 ++++++++++++------- crates/hir-def/src/nameres/path_resolution.rs | 6 ++- crates/hir-def/src/nameres/tests/macros.rs | 33 +++++++++++++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 177edcbb7f240..d1288b7b59b5b 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -44,7 +44,8 @@ use crate::{ mod_resolution::ModDir, path_resolution::ReachedFixedPoint, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind}, - BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode, + sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin, + ResolveMode, }, path::{ImportAlias, ModPath, PathKind}, per_ns::PerNs, @@ -2141,26 +2142,34 @@ impl ModCollector<'_, '_> { fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) { let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path)); + let db = self.def_collector.db; - // Case 1: try to resolve in legacy scope and expand macro_rules + // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define + // new legacy macros that create textual scopes. We need a way to resolve names in textual + // scopes without eager expansion. + + // Case 1: try to resolve macro calls with single-segment name and expand macro_rules if let Ok(res) = macro_call_as_call_id( - self.def_collector.db.upcast(), + db.upcast(), &ast_id, mac.expand_to, self.def_collector.def_map.krate, |path| { path.as_ident().and_then(|name| { - self.def_collector.def_map.with_ancestor_maps( - self.def_collector.db, - self.module_id, - &mut |map, module| { - map[module] - .scope - .get_legacy_macro(name) - .and_then(|it| it.last()) - .map(|&it| macro_id_to_def_id(self.def_collector.db, it)) - }, - ) + let def_map = &self.def_collector.def_map; + def_map + .with_ancestor_maps(db, self.module_id, &mut |map, module| { + map[module].scope.get_legacy_macro(name)?.last().copied() + }) + .or_else(|| def_map[self.module_id].scope.get(name).take_macros()) + .or_else(|| def_map.macro_use_prelude.get(name).copied()) + .filter(|&id| { + sub_namespace_match( + Some(MacroSubNs::from_id(db, id)), + Some(MacroSubNs::Bang), + ) + }) + .map(|it| macro_id_to_def_id(self.def_collector.db, it)) }) }, ) { diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 4740fd7f44994..981171013a159 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -59,7 +59,11 @@ impl ResolvePathResult { } impl PerNs { - fn filter_macro(mut self, db: &dyn DefDatabase, expected: Option) -> Self { + pub(super) fn filter_macro( + mut self, + db: &dyn DefDatabase, + expected: Option, + ) -> Self { self.macros = self.macros.filter(|&(id, _)| { let this = MacroSubNs::from_id(db, id); sub_namespace_match(Some(this), expected) diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index e795b7b9b7e6f..7eb64beb1d797 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1272,6 +1272,39 @@ pub mod prelude { ); } +#[test] +fn macro_use_prelude_is_eagerly_expanded() { + // See FIXME in `ModCollector::collect_macro_call()`. + check( + r#" +//- /main.rs crate:main deps:lib +#[macro_use] +extern crate lib; +mk_foo!(); +mod a { + foo!(); +} +//- /lib.rs crate:lib +#[macro_export] +macro_rules! mk_foo { + () => { + macro_rules! foo { + () => { struct Ok; } + } + } +} + "#, + expect![[r#" + crate + a: t + lib: t + + crate::a + Ok: t v + "#]], + ); +} + #[test] fn macro_sub_namespace() { let map = compute_crate_def_map( From cace5bb35d6c1d949acbfd57b196cd723f0cbf78 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 21:21:03 +0200 Subject: [PATCH 406/806] fix: Fix process-changes duplicating change events --- crates/rust-analyzer/src/global_state.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 9535d88454f40..08431d6488207 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -196,7 +196,7 @@ impl GlobalState { let (change, changed_files) = { let mut change = Change::new(); let (vfs, line_endings_map) = &mut *self.vfs.write(); - let mut changed_files = vfs.take_changes(); + let changed_files = vfs.take_changes(); if changed_files.is_empty() { return false; } @@ -204,7 +204,7 @@ impl GlobalState { // We need to fix up the changed events a bit. If we have a create or modify for a file // id that is followed by a delete we actually skip observing the file text from the // earlier event, to avoid problems later on. - for changed_file in &changed_files { + for changed_file in changed_files { use vfs::ChangeKind::*; file_changes @@ -240,14 +240,13 @@ impl GlobalState { )); } - changed_files.extend( - file_changes - .into_iter() - .filter(|(_, (change_kind, just_created))| { - !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) - }) - .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }), - ); + let changed_files: Vec<_> = file_changes + .into_iter() + .filter(|(_, (change_kind, just_created))| { + !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) + }) + .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }) + .collect(); // A file was added or deleted let mut has_structure_changes = false; From 51e8b8ff14de3507d5e21d80b750577da52b6fdd Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 14 May 2023 17:12:28 +0330 Subject: [PATCH 407/806] Add metrics for unevaluated constants, failed mir bodies, and failed data layouts --- .../rust-analyzer/src/cli/analysis_stats.rs | 97 ++++++++++++++++--- crates/rust-analyzer/src/cli/flags.rs | 6 +- 2 files changed, 85 insertions(+), 18 deletions(-) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index cdf40777ba381..c04691ff11ac5 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,7 +2,6 @@ //! errors. use std::{ - collections::HashMap, env, time::{SystemTime, UNIX_EPOCH}, }; @@ -16,7 +15,7 @@ use hir_def::{ hir::{ExprId, PatId}, FunctionId, }; -use hir_ty::{Interner, TyExt, TypeFlags}; +use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; use ide::{Analysis, AnalysisHost, LineCol, RootDatabase}; use ide_db::base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -122,14 +121,19 @@ impl flags::AnalysisStats { eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut funcs = Vec::new(); + let mut adts = Vec::new(); + let mut consts = Vec::new(); while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { visit_queue.extend(module.children(db)); for decl in module.declarations(db) { num_decls += 1; - if let ModuleDef::Function(f) = decl { - funcs.push(f); + match decl { + ModuleDef::Function(f) => funcs.push(f), + ModuleDef::Adt(a) => adts.push(a), + ModuleDef::Const(c) => consts.push(c), + _ => (), } } @@ -154,10 +158,13 @@ impl flags::AnalysisStats { self.run_inference(&host, db, &vfs, &funcs, verbosity); } - if self.mir_stats { - self.lower_mir(db, &funcs); + if !self.skip_mir_stats { + self.run_mir_lowering(db, &funcs, verbosity); } + self.run_data_layout(db, &adts, verbosity); + self.run_const_eval(db, &consts, verbosity); + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -193,22 +200,82 @@ impl flags::AnalysisStats { Ok(()) } - fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) { - let all = funcs.len(); + fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) { + let mut all = 0; + let mut fail = 0; + for &a in adts { + if db.generic_params(a.into()).iter().next().is_some() { + // Data types with generics don't have layout. + continue; + } + all += 1; + let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner)) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = a + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(a.name(db))) + .join("::"); + println!("Data layout for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all); + report_metric("failed data layouts", fail, "#"); + } + + fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + let mut all = 0; + let mut fail = 0; + for &c in consts { + all += 1; + let Err(e) = c.render_eval(db) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = c + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(c.name(db)) + .join("::"); + println!("Const eval for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all); + report_metric("failed const evals", fail, "#"); + } + + fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) { + let all = funcs.len() as u64; let mut fail = 0; - let mut h: HashMap = HashMap::new(); for f in funcs { - let f = FunctionId::from(*f); - let Err(e) = db.mir_body(f.into()) else { + let Err(e) = db.mir_body(FunctionId::from(*f).into()) else { continue; }; - let es = format!("{:?}", e); - *h.entry(es).or_default() += 1; + if verbosity.is_spammy() { + let full_name = f + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(f.name(db))) + .join("::"); + println!("Mir body for {full_name} failed due {e:?}"); + } fail += 1; } - let h = h.into_iter().sorted_by_key(|x| x.1).collect::>(); - eprintln!("Mir failed reasons: {:#?}", h); eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); + report_metric("mir failed bodies", fail, "#"); } fn run_inference( diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 891d99c5ad595..6b5a79b431f18 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -66,8 +66,8 @@ xflags::xflags! { optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats - /// Print the number of bodies that fail to lower to mir, in addition to failed reasons. - optional --mir-stats + /// Only type check, skip lowering to mir + optional --skip-mir-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -171,7 +171,7 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, - pub mir_stats: bool, + pub skip_mir_stats: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool, From 1bc7f8a4c66d389376e0e4e523070ca4c0186b6d Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 15 May 2023 00:05:44 +0900 Subject: [PATCH 408/806] Support `#[macro_use(name, ...)]` --- crates/hir-def/src/nameres/collector.rs | 114 ++++++++++++--------- crates/hir-def/src/nameres/tests/macros.rs | 66 ++++++++++++ 2 files changed, 134 insertions(+), 46 deletions(-) diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index d1288b7b59b5b..a47ee85da10ae 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -35,8 +35,8 @@ use crate::{ derive_macro_as_call_id, item_scope::{ImportType, PerNsGlobImports}, item_tree::{ - self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall, - MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, + self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, + MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, }, macro_call_as_call_id, macro_id_to_def_id, nameres::{ @@ -712,41 +712,28 @@ impl DefCollector<'_> { ); } - /// Import macros from `#[macro_use] extern crate`. - // FIXME: Support `#[macro_rules(macro_name, ...)]`. - fn import_macros_from_extern_crate( - &mut self, - current_module_id: LocalModuleId, - extern_crate: &item_tree::ExternCrate, - ) { - tracing::debug!( - "importing macros from extern crate: {:?} ({:?})", - extern_crate, - self.def_map.edition, - ); - - if let Some(m) = self.resolve_extern_crate(&extern_crate.name) { - if m == self.def_map.module_id(current_module_id) { - cov_mark::hit!(ignore_macro_use_extern_crate_self); - return; - } - - cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); - self.import_all_macros_exported(m.krate); - } - } - - /// Import all exported macros from another crate + /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of + /// macros to be imported. Otherwise this method imports all exported macros. /// /// Exported macros are just all macros in the root module scope. /// Note that it contains not only all `#[macro_export]` macros, but also all aliases /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_all_macros_exported(&mut self, krate: CrateId) { + fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option>) { let def_map = self.db.crate_def_map(krate); - for (name, def) in def_map[def_map.root].scope.macros() { - // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` - // macros. - self.def_map.macro_use_prelude.insert(name.clone(), def); + // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` + // macros. + let root_scope = &def_map[def_map.root].scope; + if let Some(names) = names { + for name in names { + // FIXME: Report diagnostic on 404. + if let Some(def) = root_scope.get(&name).take_macros() { + self.def_map.macro_use_prelude.insert(name, def); + } + } + } else { + for (name, def) in root_scope.macros() { + self.def_map.macro_use_prelude.insert(name.clone(), def); + } } } @@ -1537,7 +1524,7 @@ impl ModCollector<'_, '_> { if let Some(prelude_module) = self.def_collector.def_map.prelude { if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_all_macros_exported(prelude_module.krate); + self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None); } } @@ -1547,21 +1534,10 @@ impl ModCollector<'_, '_> { // // If we're not at the crate root, `macro_use`d extern crates are an error so let's just // ignore them. - // FIXME: Support `#[macro_rules(macro_name, ...)]`. if is_crate_root { for &item in items { - let ModItem::ExternCrate(id) = item else { continue; }; - let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); - if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { - let import = &self.item_tree[id]; - let attrs = self.item_tree.attrs( - self.def_collector.db, - krate, - ModItem::from(id).into(), - ); - if attrs.by_key("macro_use").exists() { - self.def_collector.import_macros_from_extern_crate(self.module_id, import); - } + if let ModItem::ExternCrate(id) = item { + self.process_macro_use_extern_crate(id); } } } @@ -1788,6 +1764,52 @@ impl ModCollector<'_, '_> { } } + fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId) { + let db = self.def_collector.db; + let attrs = self.item_tree.attrs( + db, + self.def_collector.def_map.krate, + ModItem::from(extern_crate).into(), + ); + if let Some(cfg) = attrs.cfg() { + if !self.is_cfg_enabled(&cfg) { + return; + } + } + + let target_crate = + match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) { + Some(m) => { + if m == self.def_collector.def_map.module_id(self.module_id) { + cov_mark::hit!(ignore_macro_use_extern_crate_self); + return; + } + m.krate + } + None => return, + }; + + cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); + + let mut single_imports = Vec::new(); + let hygiene = Hygiene::new_unhygienic(); + for attr in attrs.by_key("macro_use").attrs() { + let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { + // `#[macro_use]` (without any paths) found, forget collected names and just import + // all visible macros. + self.def_collector.import_macros_from_extern_crate(target_crate, None); + return; + }; + for path in paths { + if let Some(name) = path.as_ident() { + single_imports.push(name.clone()); + } + } + } + + self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports)); + } + fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { let path_attr = attrs.by_key("path").string_value(); let is_macro_use = attrs.by_key("macro_use").exists(); diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 7eb64beb1d797..56059f7cb3bb2 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -259,6 +259,72 @@ mod priv_mod { ); } +#[test] +fn macro_use_filter() { + check( + r#" +//- /main.rs crate:main deps:empty,multiple,all +#[macro_use()] +extern crate empty; + +foo_not_imported!(); + +#[macro_use(bar1)] +#[macro_use()] +#[macro_use(bar2, bar3)] +extern crate multiple; + +bar1!(); +bar2!(); +bar3!(); +bar_not_imported!(); + +#[macro_use(baz1)] +#[macro_use] +#[macro_use(baz2)] +extern crate all; + +baz1!(); +baz2!(); +baz3!(); + +//- /empty.rs crate:empty +#[macro_export] +macro_rules! foo_not_imported { () => { struct NotOkFoo; } } + +//- /multiple.rs crate:multiple +#[macro_export] +macro_rules! bar1 { () => { struct OkBar1; } } +#[macro_export] +macro_rules! bar2 { () => { struct OkBar2; } } +#[macro_export] +macro_rules! bar3 { () => { struct OkBar3; } } +#[macro_export] +macro_rules! bar_not_imported { () => { struct NotOkBar; } } + +//- /all.rs crate:all +#[macro_export] +macro_rules! baz1 { () => { struct OkBaz1; } } +#[macro_export] +macro_rules! baz2 { () => { struct OkBaz2; } } +#[macro_export] +macro_rules! baz3 { () => { struct OkBaz3; } } +"#, + expect![[r#" + crate + OkBar1: t v + OkBar2: t v + OkBar3: t v + OkBaz1: t v + OkBaz2: t v + OkBaz3: t v + all: t + empty: t + multiple: t + "#]], + ); +} + #[test] fn prelude_is_macro_use() { cov_mark::check!(prelude_is_macro_use); From 431dd32f8a57436946123f7ff83f2070c6cba192 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 14 May 2023 21:05:33 +0330 Subject: [PATCH 409/806] Unsized temporary is not an implementation error --- crates/hir-ty/src/consteval/tests.rs | 19 +++++++++++++++++++ crates/hir-ty/src/mir/lower.rs | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 12b15065ddf91..2aac1cc057d36 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2066,3 +2066,22 @@ fn type_error() { |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), ); } + +#[test] +fn unsized_local() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + const fn x() -> SomeUnknownTypeThatDereferenceToSlice { + SomeUnknownTypeThatDereferenceToSlice + } + + const GOAL: u16 = { + let y = x(); + let z: &[u16] = &y; + z[1] + }; + "#, + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), + ); +} diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 66b1d840bdc28..627c36dca92f0 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -75,6 +75,7 @@ pub enum MirLowerError { RecordLiteralWithoutPath, UnresolvedMethod(String), UnresolvedField, + UnsizedTemporary(Ty), MissingFunctionDefinition, TypeMismatch(TypeMismatch), /// This should be never happen. Type mismatch should catch everything. @@ -108,6 +109,7 @@ impl MirLowerError { } } MirLowerError::LayoutError(_) + | MirLowerError::UnsizedTemporary(_) | MirLowerError::IncompleteExpr | MirLowerError::UnaccessableLocal | MirLowerError::TraitFunctionDefinition(_, _) @@ -199,7 +201,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn temp(&mut self, ty: Ty) -> Result { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { - implementation_error!("unsized temporaries"); + return Err(MirLowerError::UnsizedTemporary(ty)); } Ok(self.result.locals.alloc(Local { ty })) } From 206a0b5bc69464bd85a905d6d195ad4355790960 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 14 May 2023 22:34:58 +0330 Subject: [PATCH 410/806] Add timer for new items --- crates/rust-analyzer/src/cli/analysis_stats.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index c04691ff11ac5..b12568b0bd963 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -201,6 +201,7 @@ impl flags::AnalysisStats { } fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) { + let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; for &a in adts { @@ -225,11 +226,13 @@ impl flags::AnalysisStats { } fail += 1; } + eprintln!("{:<20} {}", "Data layouts:", sw.elapsed()); eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all); report_metric("failed data layouts", fail, "#"); } fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; for &c in consts { @@ -250,11 +253,13 @@ impl flags::AnalysisStats { } fail += 1; } + eprintln!("{:<20} {}", "Const evaluation:", sw.elapsed()); eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all); report_metric("failed const evals", fail, "#"); } fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) { + let mut sw = self.stop_watch(); let all = funcs.len() as u64; let mut fail = 0; for f in funcs { @@ -274,6 +279,7 @@ impl flags::AnalysisStats { } fail += 1; } + eprintln!("{:<20} {}", "MIR lowering:", sw.elapsed()); eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); report_metric("mir failed bodies", fail, "#"); } From 8c191add8788cb13ac988021617f673709babb16 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Sun, 14 May 2023 19:25:23 -0500 Subject: [PATCH 411/806] the implementation!! --- CHANGELOG.md | 1 + clippy_lints/src/casts/mod.rs | 33 +++++++++++- clippy_lints/src/casts/ptr_cast_constness.rs | 35 +++++++++++++ clippy_lints/src/declared_lints.rs | 1 + tests/ui/ptr_cast_constness.fixed | 55 ++++++++++++++++++++ tests/ui/ptr_cast_constness.rs | 55 ++++++++++++++++++++ tests/ui/ptr_cast_constness.stderr | 46 ++++++++++++++++ 7 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/casts/ptr_cast_constness.rs create mode 100644 tests/ui/ptr_cast_constness.fixed create mode 100644 tests/ui/ptr_cast_constness.rs create mode 100644 tests/ui/ptr_cast_constness.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index dafa3f3a1393c..223dd69897817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4948,6 +4948,7 @@ Released 2018-09-13 [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg [`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr +[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cfeb75eed3bb9..ee2f0f8855e63 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -18,6 +18,7 @@ mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod ptr_as_ptr; +mod ptr_cast_constness; mod unnecessary_cast; mod utils; @@ -399,7 +400,7 @@ declare_clippy_lint! { /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. /// /// ### Why is this bad? - /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. /// /// ### Example @@ -422,6 +423,34 @@ declare_clippy_lint! { "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to + /// `*mut T` and `*mut T` to `*const T`. + /// + /// ### Why is this bad? + /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and + /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another + /// type. + /// + /// ### Example + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr as *mut u32; + /// let ptr = mut_ptr as *const u32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr.cast_mut(); + /// let ptr = mut_ptr.cast_const(); + /// ``` + #[clippy::version = "1.65.0"] + pub PTR_CAST_CONSTNESS, + pedantic, + "TODO" +} + declare_clippy_lint! { /// ### What it does /// Checks for casts from an enum type to an integral type which will definitely truncate the @@ -689,6 +718,7 @@ impl_lint_pass!(Casts => [ FN_TO_NUMERIC_CAST_WITH_TRUNCATION, CHAR_LIT_AS_U8, PTR_AS_PTR, + PTR_CAST_CONSTNESS, CAST_ENUM_TRUNCATION, CAST_ENUM_CONSTRUCTOR, CAST_ABS_TO_UNSIGNED, @@ -722,6 +752,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); + ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs new file mode 100644 index 0000000000000..0a95b36a59933 --- /dev/null +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -0,0 +1,35 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, TypeAndMut}; + +use super::PTR_CAST_CONSTNESS; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if_chain! { + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind(); + if !matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + then { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing its constness", + &format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a3a6f1746bcc5..beeeb322b4d19 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -89,6 +89,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::PTR_AS_PTR_INFO, + crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::UNNECESSARY_CAST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed new file mode 100644 index 0000000000000..bb7b00b50ab36 --- /dev/null +++ b/tests/ui/ptr_cast_constness.fixed @@ -0,0 +1,55 @@ +//@run-rustfix +//@aux-build:proc_macros.rs + +#![warn(clippy::ptr_cast_constness)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +#[inline_macros] +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast_mut(); + } + + let _ = ptr.cast_mut(); + let _ = mut_ptr.cast_const(); + + // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Make sure the lint is triggered inside a macro + let _ = inline!($ptr as *const i32); + + // Do not lint inside macros from external crates + let _ = external!($ptr as *const i32); +} + +#[clippy::msrv = "1.64"] +fn _msrv_1_37() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this + let _ = ptr.cast_mut(); + let _ = mut_ptr.cast_const(); +} + +#[clippy::msrv = "1.65"] +fn _msrv_1_38() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast_mut(); + let _ = mut_ptr.cast_const(); +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs new file mode 100644 index 0000000000000..a560d36d1171e --- /dev/null +++ b/tests/ui/ptr_cast_constness.rs @@ -0,0 +1,55 @@ +//@run-rustfix +//@aux-build:proc_macros.rs + +#![warn(clippy::ptr_cast_constness)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +#[inline_macros] +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *mut i32; + } + + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Make sure the lint is triggered inside a macro + let _ = inline!($ptr as *const i32); + + // Do not lint inside macros from external crates + let _ = external!($ptr as *const i32); +} + +#[clippy::msrv = "1.64"] +fn _msrv_1_37() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; +} + +#[clippy::msrv = "1.65"] +fn _msrv_1_38() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr new file mode 100644 index 0000000000000..e665e2971c495 --- /dev/null +++ b/tests/ui/ptr_cast_constness.stderr @@ -0,0 +1,46 @@ +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:20:17 + | +LL | let _ = *ptr_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` + | + = note: `-D clippy::ptr-cast-constness` implied by `-D warnings` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:23:13 + | +LL | let _ = ptr as *mut i32; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:24:13 + | +LL | let _ = mut_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:44:13 + | +LL | let _ = ptr as *mut i32; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:45:13 + | +LL | let _ = mut_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:53:13 + | +LL | let _ = ptr as *mut i32; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:54:13 + | +LL | let _ = mut_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` + +error: aborting due to 7 previous errors + From c5a914b181d4f4c06354f7afd4b66f346a92f8a8 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Sun, 14 May 2023 19:31:12 -0500 Subject: [PATCH 412/806] check msrv --- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/casts/ptr_cast_constness.rs | 13 +++++++++++-- tests/ui/ptr_cast_constness.fixed | 4 ++-- tests/ui/ptr_cast_constness.stderr | 14 +------------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index ee2f0f8855e63..07650bdd7b8d4 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -752,7 +752,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); - ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to); + ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 0a95b36a59933..059694c34c0ee 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,15 +1,24 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; +use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_semver::RustcVersion; use super::PTR_CAST_CONSTNESS; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: &Msrv, +) { if_chain! { + if msrv.meets(RustcVersion::new(1,65,0)); if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind(); if !matches!((from_mutbl, to_mutbl), diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index bb7b00b50ab36..dc7acdae69c31 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -41,8 +41,8 @@ fn _msrv_1_37() { let mut_ptr: *mut u32 = &mut 42_u32; // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this - let _ = ptr.cast_mut(); - let _ = mut_ptr.cast_const(); + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; } #[clippy::msrv = "1.65"] diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index e665e2971c495..43816c87c1907 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -18,18 +18,6 @@ error: `as` casting between raw pointers while changing its constness LL | let _ = mut_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: `as` casting between raw pointers while changing its constness - --> $DIR/ptr_cast_constness.rs:44:13 - | -LL | let _ = ptr as *mut i32; - | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` - -error: `as` casting between raw pointers while changing its constness - --> $DIR/ptr_cast_constness.rs:45:13 - | -LL | let _ = mut_ptr as *const i32; - | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` - error: `as` casting between raw pointers while changing its constness --> $DIR/ptr_cast_constness.rs:53:13 | @@ -42,5 +30,5 @@ error: `as` casting between raw pointers while changing its constness LL | let _ = mut_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors From 9e80c8571da68cf8b6f032294e767afc657d8fe8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 May 2023 11:59:03 +0200 Subject: [PATCH 413/806] internal: Inline handlers module --- crates/hir-ty/src/layout/adt.rs | 14 ++++---- crates/rust-analyzer/src/handlers.rs | 45 ----------------------- crates/rust-analyzer/src/lib.rs | 6 +++- crates/rust-analyzer/src/main_loop.rs | 52 +++++++++++++++++++++++---- 4 files changed, 58 insertions(+), 59 deletions(-) delete mode 100644 crates/rust-analyzer/src/handlers.rs diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 9dbf9b2419c16..81793e3795a64 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -38,18 +38,18 @@ pub fn layout_of_adt_query( .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate)) .collect::, _>>() }; - let (variants, is_enum, is_union, repr) = match def { + let (variants, repr) = match def { AdtId::StructId(s) => { let data = db.struct_data(s); let mut r = SmallVec::<[_; 1]>::new(); r.push(handle_variant(s.into(), &data.variant_data)?); - (r, false, false, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } AdtId::UnionId(id) => { let data = db.union_data(id); let mut r = SmallVec::new(); r.push(handle_variant(id.into(), &data.variant_data)?); - (r, false, true, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } AdtId::EnumId(e) => { let data = db.enum_data(e); @@ -63,19 +63,19 @@ pub fn layout_of_adt_query( ) }) .collect::, _>>()?; - (r, true, false, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } }; let variants = variants.iter().map(|x| x.iter().collect::>()).collect::>(); let variants = variants.iter().map(|x| x.iter().collect()).collect(); - if is_union { + if matches!(def, AdtId::UnionId(..)) { cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown) } else { cx.layout_of_struct_or_enum( &repr, &variants, - is_enum, + matches!(def, AdtId::EnumId(..)), is_unsafe_cell(db, def), layout_scalar_valid_range(db, def), |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), @@ -95,7 +95,7 @@ pub fn layout_of_adt_query( // .iter_enumerated() // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())) repr.inhibit_enum_layout_opt(), - !is_enum + !matches!(def, AdtId::EnumId(..)) && variants .iter() .next() diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs deleted file mode 100644 index c19be1965444a..0000000000000 --- a/crates/rust-analyzer/src/handlers.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! This module is responsible for implementing handlers for Language Server -//! Protocol. The majority of requests are fulfilled by calling into the -//! `ide` crate. - -use ide::AssistResolveStrategy; -use lsp_types::{Diagnostic, DiagnosticTag, NumberOrString, Url}; - -use vfs::FileId; - -use crate::{global_state::GlobalStateSnapshot, to_proto, Result}; - -pub(crate) mod request; -pub(crate) mod notification; - -pub(crate) fn publish_diagnostics( - snap: &GlobalStateSnapshot, - file_id: FileId, -) -> Result> { - let _p = profile::span("publish_diagnostics"); - let line_index = snap.file_line_index(file_id)?; - - let diagnostics: Vec = snap - .analysis - .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)? - .into_iter() - .map(|d| Diagnostic { - range: to_proto::range(&line_index, d.range), - severity: Some(to_proto::diagnostic_severity(d.severity)), - code: Some(NumberOrString::String(d.code.as_str().to_string())), - code_description: Some(lsp_types::CodeDescription { - href: Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None }, - data: None, - }) - .collect(); - Ok(diagnostics) -} diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 32dc3750fdf6b..65de4366e9f61 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -25,7 +25,6 @@ mod diff; mod dispatch; mod from_proto; mod global_state; -mod handlers; mod line_index; mod lsp_utils; mod main_loop; @@ -38,6 +37,11 @@ mod task_pool; mod to_proto; mod version; +mod handlers { + pub(crate) mod notification; + pub(crate) mod request; +} + pub mod config; pub mod lsp_ext; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ea8dbc54388a4..f06abe0763e36 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -764,15 +764,55 @@ impl GlobalState { let snapshot = self.snapshot(); self.task_pool.handle.spawn(move || { + let _p = profile::span("publish_diagnostics"); let diagnostics = subscriptions .into_iter() .filter_map(|file_id| { - crate::handlers::publish_diagnostics(&snapshot, file_id) - .ok() - .map(|diags| (file_id, diags)) + let line_index = snapshot.file_line_index(file_id).ok()?; + Some(( + file_id, + line_index, + snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()?, + )) }) - .collect::>(); - Task::Diagnostics(diagnostics) - }) + .map(|(file_id, line_index, it)| { + ( + file_id, + it.into_iter() + .map(move |d| lsp_types::Diagnostic { + range: crate::to_proto::range(&line_index, d.range), + severity: Some(crate::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String( + d.code.as_str().to_string(), + )), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&format!( + "https://rust-analyzer.github.io/manual.html#{}", + d.code.as_str() + )) + .unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: if d.unused { + Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) + } else { + None + }, + data: None, + }) + .collect::>(), + ) + }); + Task::Diagnostics(diagnostics.collect()) + }); } } From 08dc0e21af8c3b7aeeac8cb428df02d83f514083 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 May 2023 19:35:27 +0200 Subject: [PATCH 414/806] feat: Render hover actions for closure captures and sig --- crates/hir-ty/src/infer/closure.rs | 4 ++ crates/hir/src/lib.rs | 14 ++++++ crates/ide/src/highlight_related.rs | 7 +-- crates/ide/src/hover/render.rs | 47 +++++++++++++----- crates/ide/src/hover/tests.rs | 75 +++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 16 deletions(-) diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 6d03c76eb6abc..907c94ea6b193 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -166,6 +166,10 @@ impl CapturedItem { self.place.local } + pub fn ty(&self, subst: &Substitution) -> Ty { + self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst()) + } + pub fn kind(&self) -> CaptureKind { self.kind } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1fac95ae5e37c..3e5087474281b 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -3221,6 +3221,20 @@ impl Closure { .collect() } + pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let (captures, _) = infer.closure_info(&self.id); + captures + .iter() + .cloned() + .map(|capture| Type { + env: db.trait_environment_for_body(owner), + ty: capture.ty(&self.subst), + }) + .collect() + } + pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait { let owner = db.lookup_intern_closure((self.id).into()).0; let infer = &db.infer(owner); diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 3a519fe65a177..3b639104efe7c 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -54,9 +54,10 @@ pub(crate) fn highlight_related( let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?` - T![->] | T![|] => 3, - kind if kind.is_keyword() => 2, - IDENT | INT_NUMBER => 1, + T![->] => 4, + kind if kind.is_keyword() => 3, + IDENT | INT_NUMBER => 2, + T![|] => 1, _ => 0, })?; // most if not all of these should be re-implemented with information seeded from hir diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 53226db7ccd6e..4ab60c76ec1da 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -35,11 +35,11 @@ pub(super) fn type_info_of( _config: &HoverConfig, expr_or_pat: &Either, ) -> Option { - let TypeInfo { original, adjusted } = match expr_or_pat { + let ty_info = match expr_or_pat { Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - type_info(sema, _config, original, adjusted) + type_info(sema, _config, ty_info) } pub(super) fn closure_expr( @@ -47,7 +47,16 @@ pub(super) fn closure_expr( config: &HoverConfig, c: ast::ClosureExpr, ) -> Option { - let ty = &sema.type_of_expr(&c.into())?.original; + let ty = sema.type_of_expr(&c.into())?; + closure_ty(sema, config, &ty.original) +} + +fn closure_ty( + sema: &Semantics<'_, RootDatabase>, + config: &HoverConfig, + ty: &hir::Type, +) -> Option { + let c = ty.as_closure()?; let layout = if config.memory_layout { ty.layout(sema.db) .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes())) @@ -55,12 +64,10 @@ pub(super) fn closure_expr( } else { String::default() }; - let c = ty.as_closure()?; - let mut captures = c - .captured_items(sema.db) + let mut captures_rendered = c.captured_items(sema.db) .into_iter() .map(|it| { - let borrow_kind= match it.kind() { + let borrow_kind = match it.kind() { CaptureKind::SharedRef => "immutable borrow", CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))", CaptureKind::MutableRef => "mutable borrow", @@ -69,16 +76,28 @@ pub(super) fn closure_expr( format!("* `{}` by {}", it.display_place(sema.db), borrow_kind) }) .join("\n"); - if captures.trim().is_empty() { - captures = "This closure captures nothing".to_string(); + if captures_rendered.trim().is_empty() { + captures_rendered = "This closure captures nothing".to_string(); } + let mut targets: Vec = Vec::new(); + let mut push_new_def = |item: hir::ModuleDef| { + if !targets.contains(&item) { + targets.push(item); + } + }; + walk_and_push_ty(sema.db, ty, &mut push_new_def); + c.capture_types(sema.db).into_iter().for_each(|ty| { + walk_and_push_ty(sema.db, &ty, &mut push_new_def); + }); + let mut res = HoverResult::default(); + res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); res.markup = format!( "```rust\n{}{}\n{}\n```\n\n## Captures\n{}", c.display_with_id(sema.db), layout, c.display_with_impl(sema.db), - captures, + captures_rendered, ) .into(); Some(res) @@ -522,10 +541,12 @@ pub(super) fn definition( fn type_info( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, - original: hir::Type, - adjusted: Option, + config: &HoverConfig, + TypeInfo { original, adjusted }: TypeInfo, ) -> Option { + if let Some(res) = closure_ty(sema, config, &original) { + return Some(res); + } let mut res = HoverResult::default(); let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index a79e47dd67d4b..12beff3b900b6 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -114,6 +114,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { expect.assert_eq(hover.info.markup.as_str()) } +fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { + let (analysis, range) = fixture::range(ra_fixture); + let hover = analysis + .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) + .unwrap() + .unwrap(); + expect.assert_debug_eq(&hover.info.actions); +} + fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap(); @@ -294,6 +303,72 @@ fn main() { ); } +#[test] +fn hover_ranged_closure() { + check_hover_range( + r#" +struct S; +struct S2; +fn main() { + let x = &S; + let y = $0|| {x; S2}$0; +} +"#, + expect![[r#" + ```rust + {closure#0} // size = 8, align = 8 + impl FnOnce() -> S2 + ``` + + ## Captures + * `x` by move"#]], + ); + check_hover_range_actions( + r#" +struct S; +struct S2; +fn main() { + let x = &S; + let y = $0|| {x; S2}$0; +} +"#, + expect![[r#" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::S2", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 10..20, + focus_range: 17..19, + name: "S2", + kind: Struct, + description: "struct S2", + }, + }, + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..9, + focus_range: 7..8, + name: "S", + kind: Struct, + description: "struct S", + }, + }, + ], + ), + ] + "#]], + ); +} + #[test] fn hover_shows_long_type_of_an_expression() { check( From ba8bcde4f5f0033752d772d4026468e7a8747072 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 May 2023 19:45:01 +0200 Subject: [PATCH 415/806] Also render coercions for ranged type hover on closures --- crates/ide/src/hover/render.rs | 126 ++++++++++++++++++--------------- crates/ide/src/hover/tests.rs | 7 +- 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 4ab60c76ec1da..c2b9222cb9598 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -47,60 +47,8 @@ pub(super) fn closure_expr( config: &HoverConfig, c: ast::ClosureExpr, ) -> Option { - let ty = sema.type_of_expr(&c.into())?; - closure_ty(sema, config, &ty.original) -} - -fn closure_ty( - sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, - ty: &hir::Type, -) -> Option { - let c = ty.as_closure()?; - let layout = if config.memory_layout { - ty.layout(sema.db) - .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes())) - .unwrap_or_default() - } else { - String::default() - }; - let mut captures_rendered = c.captured_items(sema.db) - .into_iter() - .map(|it| { - let borrow_kind = match it.kind() { - CaptureKind::SharedRef => "immutable borrow", - CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))", - CaptureKind::MutableRef => "mutable borrow", - CaptureKind::Move => "move", - }; - format!("* `{}` by {}", it.display_place(sema.db), borrow_kind) - }) - .join("\n"); - if captures_rendered.trim().is_empty() { - captures_rendered = "This closure captures nothing".to_string(); - } - let mut targets: Vec = Vec::new(); - let mut push_new_def = |item: hir::ModuleDef| { - if !targets.contains(&item) { - targets.push(item); - } - }; - walk_and_push_ty(sema.db, ty, &mut push_new_def); - c.capture_types(sema.db).into_iter().for_each(|ty| { - walk_and_push_ty(sema.db, &ty, &mut push_new_def); - }); - - let mut res = HoverResult::default(); - res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); - res.markup = format!( - "```rust\n{}{}\n{}\n```\n\n## Captures\n{}", - c.display_with_id(sema.db), - layout, - c.display_with_impl(sema.db), - captures_rendered, - ) - .into(); - Some(res) + let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?; + closure_ty(sema, config, &TypeInfo { original, adjusted: None }) } pub(super) fn try_expr( @@ -542,11 +490,12 @@ pub(super) fn definition( fn type_info( sema: &Semantics<'_, RootDatabase>, config: &HoverConfig, - TypeInfo { original, adjusted }: TypeInfo, + ty: TypeInfo, ) -> Option { - if let Some(res) = closure_ty(sema, config, &original) { + if let Some(res) = closure_ty(sema, config, &ty) { return Some(res); - } + }; + let TypeInfo { original, adjusted } = ty; let mut res = HoverResult::default(); let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { @@ -576,6 +525,69 @@ fn type_info( Some(res) } +fn closure_ty( + sema: &Semantics<'_, RootDatabase>, + config: &HoverConfig, + TypeInfo { original, adjusted }: &TypeInfo, +) -> Option { + let c = original.as_closure()?; + let layout = if config.memory_layout { + original + .layout(sema.db) + .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes())) + .unwrap_or_default() + } else { + String::default() + }; + let mut captures_rendered = c.captured_items(sema.db) + .into_iter() + .map(|it| { + let borrow_kind = match it.kind() { + CaptureKind::SharedRef => "immutable borrow", + CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))", + CaptureKind::MutableRef => "mutable borrow", + CaptureKind::Move => "move", + }; + format!("* `{}` by {}", it.display_place(sema.db), borrow_kind) + }) + .join("\n"); + if captures_rendered.trim().is_empty() { + captures_rendered = "This closure captures nothing".to_string(); + } + let mut targets: Vec = Vec::new(); + let mut push_new_def = |item: hir::ModuleDef| { + if !targets.contains(&item) { + targets.push(item); + } + }; + walk_and_push_ty(sema.db, original, &mut push_new_def); + c.capture_types(sema.db).into_iter().for_each(|ty| { + walk_and_push_ty(sema.db, &ty, &mut push_new_def); + }); + + let adjusted = if let Some(adjusted_ty) = adjusted { + walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def); + format!( + "\nCoerced to: {}", + adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn) + ) + } else { + String::new() + }; + + let mut res = HoverResult::default(); + res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); + res.markup = format!( + "```rust\n{}{}\n{}\n```{adjusted}\n\n## Captures\n{}", + c.display_with_id(sema.db), + layout, + c.display_with_impl(sema.db), + captures_rendered, + ) + .into(); + Some(res) +} + fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option { let name = attr.name(db); let desc = format!("#[{name}]"); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 12beff3b900b6..0d8fc8a5f72f2 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -307,11 +307,12 @@ fn main() { fn hover_ranged_closure() { check_hover_range( r#" +//- minicore: fn struct S; struct S2; fn main() { let x = &S; - let y = $0|| {x; S2}$0; + let y = ($0|| {x; S2}$0).call(); } "#, expect![[r#" @@ -319,17 +320,19 @@ fn main() { {closure#0} // size = 8, align = 8 impl FnOnce() -> S2 ``` + Coerced to: &impl FnOnce() -> S2 ## Captures * `x` by move"#]], ); check_hover_range_actions( r#" +//- minicore: fn struct S; struct S2; fn main() { let x = &S; - let y = $0|| {x; S2}$0; + let y = ($0|| {x; S2}$0).call(); } "#, expect![[r#" From f6a0437e74494b3f887286540333abfc691055b4 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Mon, 15 May 2023 13:14:45 -0500 Subject: [PATCH 416/806] Update clippy_lints/src/casts/ptr_cast_constness.rs Co-authored-by: llogiq --- clippy_lints/src/casts/ptr_cast_constness.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 059694c34c0ee..f8327b04b0f63 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -21,8 +21,8 @@ pub(super) fn check( if msrv.meets(RustcVersion::new(1,65,0)); if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind(); - if !matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)); then { let sugg = Sugg::hir(cx, cast_expr, "_"); let constness = match *to_mutbl { From b87ee914fa3f74e762fa3dd24bd1c903b9330abe Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 May 2023 20:13:05 +0200 Subject: [PATCH 417/806] feat: Highlight used trait assoc items when cursor is on trait import or trait bound --- crates/ide/src/highlight_related.rs | 104 +++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 3a519fe65a177..3c4a1fe1e7470 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -4,7 +4,9 @@ use ide_db::{ defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, - syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr}, + syntax_helpers::node_ext::{ + for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr, + }, FxHashSet, RootDatabase, }; use syntax::{ @@ -39,11 +41,13 @@ pub struct HighlightRelatedConfig { // Highlights constructs related to the thing under the cursor: // // . if on an identifier, highlights all references to that identifier in the current file +// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope // . if on an `async` or `await token, highlights all yield points for that async context // . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context // . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context +// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. // -// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor. +// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor. pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, @@ -129,7 +133,7 @@ fn highlight_references( token: SyntaxToken, file_id: FileId, ) -> Option> { - let defs = find_defs(sema, token); + let defs = find_defs(sema, token.clone()); let usages = defs .iter() .filter_map(|&d| { @@ -144,6 +148,59 @@ fn highlight_references( .map(|FileReference { category, range, .. }| HighlightedRange { range, category }); let mut res = FxHashSet::default(); for &def in &defs { + // highlight trait usages + if let Definition::Trait(t) = def { + let trait_item_use_scope = (|| { + let name_ref = token.parent().and_then(ast::NameRef::cast)?; + let path = full_path_of_name_ref(&name_ref)?; + let parent = path.syntax().parent()?; + match_ast! { + match parent { + ast::UseTree(it) => it.syntax().ancestors().find(|it| { + ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind()) + }), + ast::PathType(it) => it + .syntax() + .ancestors() + .nth(2) + .and_then(ast::TypeBoundList::cast)? + .syntax() + .parent() + .filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))? + .ancestors() + .find(|it| { + ast::Item::can_cast(it.kind()) + }), + _ => None, + } + } + })(); + if let Some(trait_item_use_scope) = trait_item_use_scope { + res.extend( + t.items_with_supertraits(sema.db) + .into_iter() + .filter_map(|item| { + Definition::from(item) + .usages(sema) + .set_scope(Some(SearchScope::file_range(FileRange { + file_id, + range: trait_item_use_scope.text_range(), + }))) + .include_self_refs() + .all() + .references + .remove(&file_id) + }) + .flatten() + .map(|FileReference { category, range, .. }| HighlightedRange { + range, + category, + }), + ); + } + } + + // highlight the defs themselves match def { Definition::Local(local) => { let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); @@ -1476,6 +1533,47 @@ fn f() { let c = move$0 |y| x + y; // ^ read } +"#, + ); + } + + #[test] + fn test_trait_highlights_assoc_item_uses() { + check( + r#" +trait Foo { + //^^^ + type T; + const C: usize; + fn f() {} + fn m(&self) {} +} +impl Foo for i32 { + //^^^ + type T = i32; + const C: usize = 0; + fn f() {} + fn m(&self) {} +} +fn f(t: T) { + //^^^ + let _: T::T; + //^ + t.m(); + //^ + T::C; + //^ + T::f(); + //^ +} + +fn f2(t: T) { + //^^^ + let _: T::T; + t.m(); + T::C; + T::f(); +} "#, ); } From 77be56b691c9a2d9739d9b4e360cae148b0e0972 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 16 May 2023 13:57:36 +0100 Subject: [PATCH 418/806] fix(analysis-stats): divided by zero error --- crates/rust-analyzer/src/cli/analysis_stats.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index b12568b0bd963..f7f49188662a7 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -227,7 +227,7 @@ impl flags::AnalysisStats { fail += 1; } eprintln!("{:<20} {}", "Data layouts:", sw.elapsed()); - eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all); + eprintln!("Failed data layouts: {fail} ({}%)", percentage(fail, all)); report_metric("failed data layouts", fail, "#"); } @@ -254,7 +254,7 @@ impl flags::AnalysisStats { fail += 1; } eprintln!("{:<20} {}", "Const evaluation:", sw.elapsed()); - eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all); + eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all)); report_metric("failed const evals", fail, "#"); } @@ -280,7 +280,7 @@ impl flags::AnalysisStats { fail += 1; } eprintln!("{:<20} {}", "MIR lowering:", sw.elapsed()); - eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); + eprintln!("Mir failed bodies: {fail} ({}%)", percentage(fail, all)); report_metric("mir failed bodies", fail, "#"); } From 22599adf9bdd20050de32362ca37b27aa2c9ac6e Mon Sep 17 00:00:00 2001 From: "Alexis (Poliorcetics) Bourget" Date: Tue, 16 May 2023 15:00:23 +0200 Subject: [PATCH 419/806] fix: place type inlay hints after the item and without left-padding --- crates/ide/src/inlay_hints/bind_pat.rs | 4 ++-- crates/ide/src/inlay_hints/chaining.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 3d5122a7cf3e7..758338e6d6a3a 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -106,8 +106,8 @@ pub(super) fn hints( kind: InlayKind::Type, label, text_edit, - position: InlayHintPosition::Before, - pad_left: !has_colon, + position: InlayHintPosition::After, + pad_left: false, pad_right: false, }); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index cd1ac1e55e8b2..5b7b02700338e 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -625,8 +625,8 @@ fn main() { [ InlayHint { range: 124..130, - position: Before, - pad_left: true, + position: After, + pad_left: false, pad_right: false, kind: Type, label: [ From a6e5a912f985278d610482733e3a5bbdfccb3c04 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 16 May 2023 19:12:40 +0330 Subject: [PATCH 420/806] Expand `format_args!` with more details --- .../macro_expansion_tests/builtin_fn_macro.rs | 20 +-- crates/hir-expand/src/builtin_fn_macro.rs | 167 +++++++++++++++--- .../test_data/highlight_strings.html | 2 +- 3 files changed, 154 insertions(+), 35 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 6cb741dac719f..12e01bc69c23f 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -193,7 +193,7 @@ fn main() { format_args!("{} {:?}", arg1(a, b, c), arg2); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -201,9 +201,9 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Debug::fmt), ]); } -"#]], +"##]], ); } @@ -221,7 +221,7 @@ fn main() { format_args!("{} {:?}", a::(), b); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -229,9 +229,9 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); } -"#]], +"##]], ); } @@ -250,7 +250,7 @@ fn main() { format_args!/*+errors*/("{} {:?}", a.); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -259,10 +259,10 @@ macro_rules! format_args { fn main() { let _ = - /* parse error: expected field name or number */ -$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), ]); + /* error: no rule matches input tokens *//* parse error: expected field name or number */ +$crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(), $crate::fmt::Debug::fmt), ]); } -"#]], +"##]], ); } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 9c8dc4ed1fca7..3f9ea96545ce2 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,9 +1,13 @@ //! Builtin macro +use std::mem; + +use ::tt::Ident; use base_db::{AnchoredPath, Edition, FileId}; use cfg::CfgExpr; use either::Either; use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap}; +use rustc_hash::FxHashMap; use syntax::{ ast::{self, AstToken}, SmolStr, @@ -90,11 +94,6 @@ register_builtin! { (module_path, ModulePath) => module_path_expand, (assert, Assert) => assert_expand, (stringify, Stringify) => stringify_expand, - (format_args, FormatArgs) => format_args_expand, - (const_format_args, ConstFormatArgs) => format_args_expand, - // format_args_nl only differs in that it adds a newline in the end, - // so we use the same stub expansion for now - (format_args_nl, FormatArgsNl) => format_args_expand, (llvm_asm, LlvmAsm) => asm_expand, (asm, Asm) => asm_expand, (global_asm, GlobalAsm) => global_asm_expand, @@ -106,6 +105,9 @@ register_builtin! { (trace_macros, TraceMacros) => trace_macros_expand, EAGER: + (format_args, FormatArgs) => format_args_expand, + (const_format_args, ConstFormatArgs) => format_args_expand, + (format_args_nl, FormatArgsNl) => format_args_nl_expand, (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, @@ -232,42 +234,159 @@ fn file_expand( } fn format_args_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { + format_args_expand_general(db, id, tt, "") + .map(|x| ExpandedEager { subtree: x, included_file: None }) +} + +fn format_args_nl_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { + format_args_expand_general(db, id, tt, "\\n") + .map(|x| ExpandedEager { subtree: x, included_file: None }) +} + +fn format_args_expand_general( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, + end_string: &str, ) -> ExpandResult { - // We expand `format_args!("", a1, a2)` to - // ``` - // $crate::fmt::Arguments::new_v1(&[], &[ - // $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt), - // $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt), - // ]) - // ```, - // which is still not really correct, but close enough for now - let mut args = parse_exprs_with_sep(tt, ','); + let args = parse_exprs_with_sep(tt, ','); + + let expand_error = + ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); if args.is_empty() { - return ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); + return expand_error; } - for arg in &mut args { + let mut key_args = FxHashMap::default(); + let mut args = args.into_iter().filter_map(|mut arg| { // Remove `key =`. if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { // but not with `==` - if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' ) + if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { - arg.token_trees.drain(..2); + let key = arg.token_trees.drain(..2).next().unwrap(); + key_args.insert(key.to_string(), arg); + return None; + } + } + Some(arg) + }).collect::>().into_iter(); + // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`) + let format_subtree = args.next().unwrap(); + let format_string = (|| { + let token_tree = format_subtree.token_trees.get(0)?; + match token_tree { + tt::TokenTree::Leaf(l) => match l { + tt::Leaf::Literal(l) => { + let text = l.text.strip_prefix('"')?.strip_suffix('"')?; + let span = l.span; + Some((text, span)) + } + _ => None, + }, + tt::TokenTree::Subtree(_) => None, + } + })(); + let Some((format_string, _format_string_span)) = format_string else { + return expand_error; + }; + let mut format_iter = format_string.chars().peekable(); + let mut parts = vec![]; + let mut last_part = String::new(); + let mut arg_tts = vec![]; + let mut err = None; + while let Some(c) = format_iter.next() { + // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info + match c { + '{' => { + if format_iter.peek() == Some(&'{') { + format_iter.next(); + last_part.push('{'); + continue; + } + let mut argument = String::new(); + while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) { + argument.push(match format_iter.next() { + Some(c) => c, + None => return expand_error, + }); + } + let format_spec = match format_iter.next().unwrap() { + '}' => "".to_owned(), + ':' => { + let mut s = String::new(); + while let Some(c) = format_iter.next() { + if c == '}' { + break; + } + s.push(c); + } + s + } + _ => unreachable!(), + }; + parts.push(mem::take(&mut last_part)); + let arg_tree = if argument.is_empty() { + match args.next() { + Some(x) => x, + None => { + err = Some(mbe::ExpandError::NoMatchingRule.into()); + tt::Subtree::empty() + } + } + } else if let Some(tree) = key_args.get(&argument) { + tree.clone() + } else { + // FIXME: we should pick the related substring of the `_format_string_span` as the span. You + // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval. + let ident = Ident::new(argument, tt::TokenId::unspecified()); + quote!(#ident) + }; + let formatter = match &*format_spec { + "?" => quote!(#DOLLAR_CRATE::fmt::Debug::fmt), + "" => quote!(#DOLLAR_CRATE::fmt::Display::fmt), + _ => { + // FIXME: implement the rest and return expand error here + quote!(#DOLLAR_CRATE::fmt::Display::fmt) + } + }; + arg_tts.push( + quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }, + ); + } + '}' => { + if format_iter.peek() == Some(&'}') { + format_iter.next(); + last_part.push('}'); + } else { + return expand_error; + } } + _ => last_part.push(c), } } - let _format_string = args.remove(0); - let arg_tts = args.into_iter().flat_map(|arg| { - quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } - }.token_trees); + last_part += end_string; + if !last_part.is_empty() { + parts.push(last_part); + } + let part_tts = parts.into_iter().map(|x| { + let l = tt::Literal { span: tt::TokenId::unspecified(), text: format!("\"{}\"", x).into() }; + quote!(#l ,) + }); + let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); let expanded = quote! { - #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts]) + #DOLLAR_CRATE::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) }; - ExpandResult::ok(expanded) + ExpandResult { value: expanded, err } } fn asm_expand( diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 6acc62e0f1eb4..327e1502d19e0 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -171,5 +171,5 @@ assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); asm!("mov eax, {0}"); - format_args!(concat!("{}"), "{}"); + format_args!(concat!("{}"), "{}"); } \ No newline at end of file From 4ff1cd365df96c4d0aef5b899ac248ff11799c36 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Tue, 16 May 2023 11:20:00 -0500 Subject: [PATCH 421/806] add description and rename msrv tests --- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/casts/ptr_cast_constness.rs | 4 ++-- clippy_utils/src/msrvs.rs | 2 +- tests/ui/ptr_cast_constness.fixed | 4 ++-- tests/ui/ptr_cast_constness.rs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 07650bdd7b8d4..a2f8546d3a661 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -448,7 +448,7 @@ declare_clippy_lint! { #[clippy::version = "1.65.0"] pub PTR_CAST_CONSTNESS, pedantic, - "TODO" + "casting using `as` from and to raw pointers to change constness when specialized methods apply" } declare_clippy_lint! { diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index f8327b04b0f63..ab015f8822e1b 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,3 +1,4 @@ +use clippy_utils::msrvs::POINTER_CAST_CONSTNESS; use clippy_utils::sugg::Sugg; use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv}; use if_chain::if_chain; @@ -5,7 +6,6 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeAndMut}; -use rustc_semver::RustcVersion; use super::PTR_CAST_CONSTNESS; @@ -18,7 +18,7 @@ pub(super) fn check( msrv: &Msrv, ) { if_chain! { - if msrv.meets(RustcVersion::new(1,65,0)); + if msrv.meets(POINTER_CAST_CONSTNESS); if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind(); if matches!((from_mutbl, to_mutbl), diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index e05de2dc99c05..6f102308f0bf6 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -20,7 +20,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,68,0 { PATH_MAIN_SEPARATOR_STR } - 1,65,0 { LET_ELSE } + 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 1,55,0 { SEEK_REWIND } diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index dc7acdae69c31..24de573d0838e 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -36,7 +36,7 @@ fn main() { } #[clippy::msrv = "1.64"] -fn _msrv_1_37() { +fn _msrv_1_64() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -46,7 +46,7 @@ fn _msrv_1_37() { } #[clippy::msrv = "1.65"] -fn _msrv_1_38() { +fn _msrv_1_65() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index a560d36d1171e..63d973a9fca87 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -36,7 +36,7 @@ fn main() { } #[clippy::msrv = "1.64"] -fn _msrv_1_37() { +fn _msrv_1_64() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -46,7 +46,7 @@ fn _msrv_1_37() { } #[clippy::msrv = "1.65"] -fn _msrv_1_38() { +fn _msrv_1_65() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; From 6d2ba95d0099f1a656da44c7f868be0074e5b5e1 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 14 May 2023 19:10:52 +0200 Subject: [PATCH 422/806] suggest `Option::as_deref(_mut)` --- .../src/traits/error_reporting/suggestions.rs | 105 ++++++++++++++++-- .../suggest-option-asderef-unfixable.rs | 40 +++++++ .../suggest-option-asderef-unfixable.stderr | 96 ++++++++++++++++ .../suggest-option-asderef.fixed | 30 +++++ .../suggest-option-asderef.rs | 30 +++++ .../suggest-option-asderef.stderr | 63 +++++++++++ 6 files changed, 355 insertions(+), 9 deletions(-) create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr create mode 100644 tests/ui/mismatched_types/suggest-option-asderef.fixed create mode 100644 tests/ui/mismatched_types/suggest-option-asderef.rs create mode 100644 tests/ui/mismatched_types/suggest-option-asderef.stderr 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 49b309abcda3a..9f2740a3898db 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, Style, + ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -27,6 +27,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; use rustc_middle::hir::map; +use rustc_middle::query::Key; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, @@ -357,6 +358,15 @@ pub trait TypeErrCtxtExt<'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ); + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ); + fn note_function_argument_obligation( &self, body_id: LocalDefId, @@ -3660,15 +3670,92 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.replace_span_with(path.ident.span, true); } } - if let Some(Node::Expr(hir::Expr { - kind: - hir::ExprKind::Call(hir::Expr { span, .. }, _) - | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..), - .. - })) = hir.find(call_hir_id) + + if let Some(Node::Expr(expr)) = hir.find(call_hir_id) { + if let hir::ExprKind::Call(hir::Expr { span, .. }, _) + | hir::ExprKind::MethodCall( + hir::PathSegment { ident: Ident { span, .. }, .. }, + .., + ) = expr.kind + { + if Some(*span) != err.span.primary_span() { + err.span_label(*span, "required by a bound introduced by this call"); + } + } + + if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind { + self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr); + } + } + } + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ) { + let tcx = self.tcx; + let infcx = self.infcx; + let Some(typeck_results) = self.typeck_results.as_ref() else { return }; + + // Make sure we're dealing with the `Option` type. + let Some(ty_adt_did) = typeck_results.expr_ty_adjusted(expr).ty_adt_id() else { return }; + if !tcx.is_diagnostic_item(sym::Option, ty_adt_did) { + return; + } + + // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`, + // then suggest `Option::as_deref(_mut)` if `U` can deref to `T` + if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { trait_ref, .. })) + = failed_pred.kind().skip_binder() + && tcx.is_fn_trait(trait_ref.def_id) + && let [self_ty, found_ty] = trait_ref.substs.as_slice() + && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn()) + && let fn_sig @ ty::FnSig { + abi: abi::Abi::Rust, + c_variadic: false, + unsafety: hir::Unsafety::Normal, + .. + } = fn_ty.fn_sig(tcx).skip_binder() + + // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T` + && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind()) + && !target_ty.has_escaping_bound_vars() + + // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U` + && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind) + && let &[found_ty] = tys.as_slice() + && !found_ty.has_escaping_bound_vars() + + // Extract `::Target` assoc type and check that it is `T` + && let Some(deref_target_did) = tcx.lang_items().deref_target() + && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) + && let Ok(deref_target) = tcx.try_normalize_erasing_regions(param_env, projection) + && deref_target == target_ty { - if Some(*span) != err.span.primary_span() { - err.span_label(*span, "required by a bound introduced by this call"); + let help = if let hir::Mutability::Mut = needs_mut + && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() + && infcx + .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env) + .must_apply_modulo_regions() + { + Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()")) + } else if let hir::Mutability::Not = needs_mut { + Some(("call `Option::as_deref()` first", ".as_deref()")) + } else { + None + }; + + if let Some((msg, sugg)) = help { + err.span_suggestion_with_style( + expr.span.shrink_to_hi(), + msg, + sugg, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways + ); } } } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs new file mode 100644 index 0000000000000..cc9ba5514fef1 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -0,0 +1,40 @@ +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + Some(()) +} + +fn no_args() -> Option<()> { + Some(()) +} + +fn generic_ref(_: &T) -> Option<()> { + Some(()) +} + +extern "C" fn takes_str_but_wrong_abi(_: &str) -> Option<()> { + Some(()) +} + +unsafe fn takes_str_but_unsafe(_: &str) -> Option<()> { + Some(()) +} + +struct TypeWithoutDeref; + +fn main() { + let _ = produces_string().and_then(takes_str_but_too_many_refs); + //~^ ERROR type mismatch in function arguments + let _ = produces_string().and_then(takes_str_but_wrong_abi); + //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + let _ = produces_string().and_then(takes_str_but_unsafe); + //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + let _ = produces_string().and_then(no_args); + //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments + let _ = produces_string().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr new file mode 100644 index 0000000000000..079909eb48d1d --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -0,0 +1,96 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-unfixable.rs:28:40 + | +LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + | ------------------------------------------------------ found signature defined here +... +LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a, 'b> fn(&'a &'b str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + --> $DIR/suggest-option-asderef-unfixable.rs:30:40 + | +LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + --> $DIR/suggest-option-asderef-unfixable.rs:32:40 + | +LL | let _ = produces_string().and_then(takes_str_but_unsafe); + | -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + = note: unsafe function cannot be called generically without an unsafe block +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0593]: function is expected to take 1 argument, but it takes 0 arguments + --> $DIR/suggest-option-asderef-unfixable.rs:34:40 + | +LL | fn no_args() -> Option<()> { + | -------------------------- takes 0 arguments +... +LL | let _ = produces_string().and_then(no_args); + | -------- ^^^^^^^ expected function that takes 1 argument + | | + | required by a bound introduced by this call + | +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-unfixable.rs:36:40 + | +LL | fn generic_ref(_: &T) -> Option<()> { + | -------------------------------------- found signature defined here +... +LL | let _ = produces_string().and_then(generic_ref); + | -------- ^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a _) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn generic_ref(_: &T) -> Option<()> { +LL + fn generic_ref(_: T) -> Option<()> { + | + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-unfixable.rs:38:45 + | +LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + | ------------------------------------------------------ found signature defined here +... +LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(TypeWithoutDeref) -> _` + found function signature `for<'a, 'b> fn(&'a &'b str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0593, E0631. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef.fixed b/tests/ui/mismatched_types/suggest-option-asderef.fixed new file mode 100644 index 0000000000000..08805999341ff --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str(_: &str) -> Option<()> { + Some(()) +} + +fn takes_str_mut(_: &mut str) -> Option<()> { + Some(()) +} + +fn generic(_: T) -> Option<()> { + Some(()) +} + +fn main() { + let _: Option<()> = produces_string().as_deref().and_then(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().as_deref().map(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref_mut()` first + let _ = produces_string().and_then(generic); +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef.rs b/tests/ui/mismatched_types/suggest-option-asderef.rs new file mode 100644 index 0000000000000..3cfb2ffa828c6 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.rs @@ -0,0 +1,30 @@ +// run-rustfix + +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str(_: &str) -> Option<()> { + Some(()) +} + +fn takes_str_mut(_: &mut str) -> Option<()> { + Some(()) +} + +fn generic(_: T) -> Option<()> { + Some(()) +} + +fn main() { + let _: Option<()> = produces_string().and_then(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().map(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().map(takes_str_mut); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref_mut()` first + let _ = produces_string().and_then(generic); +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr new file mode 100644 index 0000000000000..46da19d2bf4f2 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -0,0 +1,63 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:20:52 + | +LL | fn takes_str(_: &str) -> Option<()> { + | ----------------------------------- found signature defined here +... +LL | let _: Option<()> = produces_string().and_then(takes_str); + | -------- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref()` first + | +LL | let _: Option<()> = produces_string().as_deref().and_then(takes_str); + | +++++++++++ + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:23:55 + | +LL | fn takes_str(_: &str) -> Option<()> { + | ----------------------------------- found signature defined here +... +LL | let _: Option> = produces_string().map(takes_str); + | --- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a str) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref()` first + | +LL | let _: Option> = produces_string().as_deref().map(takes_str); + | +++++++++++ + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:26:55 + | +LL | fn takes_str_mut(_: &mut str) -> Option<()> { + | ------------------------------------------- found signature defined here +... +LL | let _: Option> = produces_string().map(takes_str_mut); + | --- ^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a mut str) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref_mut()` first + | +LL | let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); + | +++++++++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0631`. From c12ede8c348542f1a67dc9c65c50925a2c5c22e1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 May 2023 22:15:39 +0200 Subject: [PATCH 423/806] fix: Discriminant hints only render for datacarrying enums with primitive repr --- crates/hir/src/lib.rs | 5 ++ crates/ide/src/inlay_hints/discriminant.rs | 88 ++++++++++---------- crates/rust-analyzer/src/handlers/request.rs | 2 +- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1fac95ae5e37c..9ab1f4f0a6951 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1075,6 +1075,10 @@ impl Enum { db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() } + pub fn repr(self, db: &dyn HirDatabase) -> Option { + db.enum_data(self.id).repr + } + pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::from_def(db, self.id) } @@ -1112,6 +1116,7 @@ impl Enum { ) } + /// Returns true if at least one variant of this enum is a non-unit variant. pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index 9bff98f6110cb..c4d2ac75cfa97 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -20,21 +20,23 @@ pub(super) fn enum_hints( _: FileId, enum_: ast::Enum, ) -> Option<()> { - let enabled = match config.discriminant_hints { - DiscriminantHints::Always => true, - DiscriminantHints::Fieldless => { - !sema.to_def(&enum_)?.is_data_carrying(sema.db) - || enum_.variant_list()?.variants().any(|v| v.expr().is_some()) - } - DiscriminantHints::Never => false, - }; - if !enabled { + if let DiscriminantHints::Never = config.discriminant_hints { + return None; + } + + let def = sema.to_def(&enum_)?; + let data_carrying = def.is_data_carrying(sema.db); + if matches!(config.discriminant_hints, DiscriminantHints::Fieldless) && data_carrying { + return None; + } + // data carrying enums without a primitive repr have no stable discriminants + if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) { return None; } for variant in enum_.variant_list()?.variants() { variant_hints(acc, sema, &variant); } - None + Some(()) } fn variant_hints( @@ -91,7 +93,6 @@ fn variant_hints( Some(()) } - #[cfg(test)] mod tests { use crate::inlay_hints::{ @@ -123,30 +124,30 @@ mod tests { check_discriminants( r#" enum Enum { - Variant, -//^^^^^^^ = 0$ - Variant1, -//^^^^^^^^ = 1$ - Variant2, -//^^^^^^^^ = 2$ - Variant5 = 5, - Variant6, -//^^^^^^^^ = 6$ + Variant, +// ^^^^^^^ = 0$ + Variant1, +// ^^^^^^^^ = 1$ + Variant2, +// ^^^^^^^^ = 2$ + Variant5 = 5, + Variant6, +// ^^^^^^^^ = 6$ } "#, ); check_discriminants_fieldless( r#" enum Enum { - Variant, -//^^^^^^^ = 0 - Variant1, -//^^^^^^^^ = 1 - Variant2, -//^^^^^^^^ = 2 - Variant5 = 5, - Variant6, -//^^^^^^^^ = 6 + Variant, +// ^^^^^^^ = 0 + Variant1, +// ^^^^^^^^ = 1 + Variant2, +// ^^^^^^^^ = 2 + Variant5 = 5, + Variant6, +// ^^^^^^^^ = 6 } "#, ); @@ -156,26 +157,23 @@ enum Enum { fn datacarrying_mixed() { check_discriminants( r#" +#[repr(u8)] enum Enum { Variant(), - //^^^^^^^^^ = 0 +// ^^^^^^^^^ = 0 Variant1, - //^^^^^^^^ = 1 +// ^^^^^^^^ = 1 Variant2 {}, - //^^^^^^^^^^^ = 2 +// ^^^^^^^^^^^ = 2 Variant3, - //^^^^^^^^ = 3 +// ^^^^^^^^ = 3 Variant5 = 5, Variant6, - //^^^^^^^^ = 6 +// ^^^^^^^^ = 6 } "#, ); - } - - #[test] - fn datacarrying_mixed_fieldless_set() { - check_discriminants_fieldless( + check_discriminants( r#" enum Enum { Variant(), @@ -187,20 +185,20 @@ enum Enum { } "#, ); + } + + #[test] + fn datacarrying_mixed_fieldless_set() { check_discriminants_fieldless( r#" +#[repr(u8)] enum Enum { Variant(), - //^^^^^^^^^ = 0 Variant1, - //^^^^^^^^ = 1 Variant2 {}, - //^^^^^^^^^^^ = 2 Variant3, - //^^^^^^^^ = 3 - Variant5 = 5, + Variant5, Variant6, - //^^^^^^^^ = 6 } "#, ); diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 1e0cb2ec2fbf8..9c198eefc757f 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -102,7 +102,7 @@ pub(crate) fn handle_analyzer_status( .collect::>() ); } - format_to!(buf, "\nVfs memory usage: {}\n", snap.vfs_memory_usage()); + format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _)); buf.push_str("\nAnalysis:\n"); buf.push_str( &snap From 478705baf5a64274edd8b2660d5e2be83edc780d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 May 2023 22:47:27 +0200 Subject: [PATCH 424/806] fix: Diagnose non-value return and break type mismatches --- crates/hir-ty/src/infer/coerce.rs | 28 +++++++++--- crates/hir-ty/src/infer/expr.rs | 43 ++++++++++++------- .../src/handlers/type_mismatch.rs | 14 ++++++ 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index e9c94449cfc71..05a476f632dc2 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -50,6 +50,13 @@ fn success( Ok(InferOk { goals, value: (adj, target) }) } +pub(super) enum CoercionCause { + // FIXME: Make better use of this. Right now things like return and break without a value + // use it to point to themselves, causing us to report a mismatch on those expressions even + // though technically they themselves are `!` + Expr(ExprId), +} + #[derive(Clone, Debug)] pub(super) struct CoerceMany { expected_ty: Ty, @@ -90,8 +97,12 @@ impl CoerceMany { } } - pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) { - self.coerce(ctx, None, &ctx.result.standard_types.unit.clone()) + pub(super) fn coerce_forced_unit( + &mut self, + ctx: &mut InferenceContext<'_>, + cause: CoercionCause, + ) { + self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause) } /// Merge two types from different branches, with possible coercion. @@ -106,6 +117,7 @@ impl CoerceMany { ctx: &mut InferenceContext<'_>, expr: Option, expr_ty: &Ty, + cause: CoercionCause, ) { let expr_ty = ctx.resolve_ty_shallow(expr_ty); self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty); @@ -153,11 +165,13 @@ impl CoerceMany { } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) { self.final_ty = Some(res); } else { - if let Some(id) = expr { - ctx.result.type_mismatches.insert( - id.into(), - TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() }, - ); + match cause { + CoercionCause::Expr(id) => { + ctx.result.type_mismatches.insert( + id.into(), + TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() }, + ); + } } cov_mark::hit!(coerce_merge_fail_fallback); } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 96218e4fb094d..353bf6568c107 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -26,7 +26,10 @@ use crate::{ autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, infer::{ - coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind, + coerce::{CoerceMany, CoercionCause}, + find_continuable, + pat::contains_explicit_ref_binding, + BreakableKind, }, lang_items::lang_items_for_bin_op, lower::{ @@ -132,24 +135,28 @@ impl<'a> InferenceContext<'a> { ); let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let mut both_arms_diverge = Diverges::Always; let then_ty = self.infer_expr_inner(then_branch, expected); - both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); + let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); - coerce.coerce(self, Some(then_branch), &then_ty); + coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected); - coerce.coerce(self, Some(else_branch), &else_ty); + let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + coerce.coerce( + self, + Some(else_branch), + &else_ty, + CoercionCause::Expr(else_branch), + ); + self.diverges = condition_diverges | then_diverges & else_diverges; } None => { - coerce.coerce_forced_unit(self); + coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr)); + self.diverges = condition_diverges; } } - both_arms_diverge &= self.diverges; - - self.diverges = condition_diverges | both_arms_diverge; coerce.complete(self) } @@ -444,7 +451,7 @@ impl<'a> InferenceContext<'a> { let arm_ty = self.infer_expr_inner(arm.expr, &expected); all_arms_diverge &= self.diverges; - coerce.coerce(self, Some(arm.expr), &arm_ty); + coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); } self.diverges = matchee_diverges | all_arms_diverge; @@ -492,7 +499,11 @@ impl<'a> InferenceContext<'a> { match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { - coerce.coerce(self, expr, &val_ty); + let cause = match expr { + Some(expr) => CoercionCause::Expr(expr), + None => CoercionCause::Expr(tgt_expr), + }; + coerce.coerce(self, expr, &val_ty, cause); // Avoiding borrowck let ctxt = find_breakable(&mut self.breakables, label) @@ -512,7 +523,7 @@ impl<'a> InferenceContext<'a> { } self.result.standard_types.never.clone() } - &Expr::Return { expr } => self.infer_expr_return(expr), + &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), Expr::Yield { expr } => { if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { if let Some(expr) = expr { @@ -952,7 +963,7 @@ impl<'a> InferenceContext<'a> { let mut coerce = CoerceMany::new(elem_ty); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected); - coerce.coerce(self, Some(expr), &cur_elem_ty); + coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); } ( coerce.complete(self), @@ -997,18 +1008,18 @@ impl<'a> InferenceContext<'a> { .expected_ty(); let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty)); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, Some(expr), &return_expr_ty); + coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); self.return_coercion = Some(coerce_many); } - fn infer_expr_return(&mut self, expr: Option) -> Ty { + fn infer_expr_return(&mut self, ret: ExprId, expr: Option) -> Ty { match self.return_coercion { Some(_) => { if let Some(expr) = expr { self.infer_return(expr); } else { let mut coerce = self.return_coercion.take().unwrap(); - coerce.coerce_forced_unit(self); + coerce.coerce_forced_unit(self, CoercionCause::Expr(ret)); self.return_coercion = Some(coerce); } } diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index c462a16e36276..a5359741ac950 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -678,6 +678,20 @@ struct Bar { f2: &[u16], f3: dyn Debug, } +"#, + ); + } + + #[test] + fn return_no_value() { + check_diagnostics( + r#" +fn f() -> i32 { + return; + // ^^^^^^ error: expected i32, found () + 0 +} +fn g() { return; } "#, ); } From 97467e4aa39d20273014195bb7362d6bace047ea Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Tue, 16 May 2023 16:29:31 -0500 Subject: [PATCH 425/806] change `clippy::version` to 1.71.0 --- clippy_lints/src/casts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index a2f8546d3a661..d62639caca1b6 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -445,7 +445,7 @@ declare_clippy_lint! { /// let mut_ptr = ptr.cast_mut(); /// let ptr = mut_ptr.cast_const(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.71.0"] pub PTR_CAST_CONSTNESS, pedantic, "casting using `as` from and to raw pointers to change constness when specialized methods apply" From a2fba7c67ec46afc78701759719ffabf4218ad3d Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 17 May 2023 01:15:04 +0330 Subject: [PATCH 426/806] Add test for eager expanding of `format_args` --- .../macro_expansion_tests/builtin_fn_macro.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 12e01bc69c23f..0beab57eb738a 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -235,6 +235,40 @@ fn main() { ); } +#[test] +fn test_format_args_expand_eager() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b); +} +"#, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + $crate::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[$crate::fmt::ArgumentV1::new(&(2), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); +} +"##]], + ); +} + #[test] fn test_format_args_expand_with_broken_member_access() { check( From fd034bea1a13cf557f9fa3446f8f31fa18c084f9 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 17 May 2023 01:27:45 +0330 Subject: [PATCH 427/806] Fix evaluating negative for floating point types --- crates/hir-ty/src/consteval/tests.rs | 4 +++ crates/hir-ty/src/mir/eval.rs | 39 ++++++++++++++++++---------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 12b15065ddf91..aa84103dd8ad6 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -113,6 +113,10 @@ fn floating_point() { r#"const GOAL: f32 = 2.0 + 3.0 * 5.5 - 8.;"#, i128::from_le_bytes(pad16(&f32::to_le_bytes(10.5), true)), ); + check_number( + r#"const GOAL: f32 = -90.0 + 36.0;"#, + i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)), + ); } #[test] diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 8ce16df829587..c55ecb056c05a 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -759,25 +759,38 @@ impl Evaluator<'_> { 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]; + if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { + match f { + chalk_ir::FloatTy::F32 => { + let c = -from_bytes!(f32, c); + Owned(c.to_le_bytes().into()) + } + chalk_ir::FloatTy::F64 => { + let c = -from_bytes!(f32, c); + Owned(c.to_le_bytes().into()) + } + } } 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; + 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) } - Owned(c) } Rvalue::CheckedBinaryOp(op, lhs, rhs) => { let lc = self.eval_operand(lhs, locals)?; From 68a74decb683929f59588e07636e44c40434d6bc Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Wed, 17 May 2023 17:45:44 +0900 Subject: [PATCH 428/806] Process `macro_use` prelude in semantic scope resolver --- crates/hir-def/src/find_path.rs | 2 +- crates/hir-def/src/nameres.rs | 8 ++++++-- crates/hir-def/src/resolver.rs | 5 ++++- crates/ide-completion/src/tests/flyimport.rs | 21 ++++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index b401762255ef8..1e299fecc9d81 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -183,7 +183,7 @@ fn find_path_for_module( // - if the item is the crate root of a dependency crate, return the name from the extern prelude let root_def_map = crate_root.def_map(db); - for (name, &def_id) in root_def_map.extern_prelude() { + for (name, def_id) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 39a56814ed88b..176637f9d0dde 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -355,8 +355,12 @@ impl DefMap { self.prelude } - pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { - self.extern_prelude.iter() + pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { + self.extern_prelude.iter().map(|(name, def)| (name, *def)) + } + + pub(crate) fn macro_use_prelude(&self) -> impl Iterator + '_ { + self.macro_use_prelude.iter().map(|(name, def)| (name, *def)) } pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 4bec2b4dea429..afa3b33cc9fea 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -461,7 +461,10 @@ impl Resolver { res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); }) }); - def_map.extern_prelude().for_each(|(name, &def)| { + def_map.macro_use_prelude().for_each(|(name, def)| { + res.add(name, ScopeDef::ModuleDef(def.into())); + }); + def_map.extern_prelude().for_each(|(name, def)| { res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def))); }); BUILTIN_SCOPE.iter().for_each(|(name, &def)| { diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index d727320b51489..8c038c0fbaa1c 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1265,3 +1265,24 @@ macro_rules! define_struct { "#]], ); } + +#[test] +fn macro_use_prelude_is_in_scope() { + check( + r#" +//- /main.rs crate:main deps:dep +#[macro_use] +extern crate dep; + +fn main() { + print$0 +} +//- /lib.rs crate:dep +#[macro_export] +macro_rules! println { + () => {} +} +"#, + expect![""], + ) +} From 83a4b0987f54afbfaad8fed61882231a6632bc71 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 18 May 2023 07:56:04 +0200 Subject: [PATCH 429/806] Fix formatting of if let chain --- clippy_lints/src/manual_let_else.rs | 93 +++++++++++++++-------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 3f8b42ffe8053..855bbf4c668b8 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -77,53 +77,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { local.els.is_none() && local.ty.is_none() && init.span.ctxt() == stmt.span.ctxt() && - let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { - match if_let_or_match { - IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { - if expr_is_simple_identity(let_pat, if_then); - if let Some(if_else) = if_else; - if expr_diverges(cx, if_else); - then { - emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); - } - }, - IfLetOrMatch::Match(match_expr, arms, source) => { - if self.matches_behaviour == MatchLintBehaviour::Never { - return; - } - if source != MatchSource::Normal { - return; - } - // Any other number than two arms doesn't (necessarily) - // have a trivial mapping to let else. - if arms.len() != 2 { - return; - } - // Guards don't give us an easy mapping either - if arms.iter().any(|arm| arm.guard.is_some()) { - return; - } - let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; - let diverging_arm_opt = arms - .iter() - .enumerate() - .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); - let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; - // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. - // However, if it arrives in second position, its pattern may cover some cases already covered - // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { - return; - } - let pat_arm = &arms[1 - idx]; - if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { - return; - } + let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) + { + match if_let_or_match { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { + if expr_is_simple_identity(let_pat, if_then); + if let Some(if_else) = if_else; + if expr_diverges(cx, if_else); + then { + emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); + } + }, + IfLetOrMatch::Match(match_expr, arms, source) => { + if self.matches_behaviour == MatchLintBehaviour::Never { + return; + } + if source != MatchSource::Normal { + return; + } + // Any other number than two arms doesn't (necessarily) + // have a trivial mapping to let else. + if arms.len() != 2 { + return; + } + // Guards don't give us an easy mapping either + if arms.iter().any(|arm| arm.guard.is_some()) { + return; + } + let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; + let diverging_arm_opt = arms + .iter() + .enumerate() + .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. + // However, if it arrives in second position, its pattern may cover some cases already covered + // by the diverging one. + // TODO: accept the non-diverging arm as a second position if patterns are disjointed. + if idx == 0 { + return; + } + let pat_arm = &arms[1 - idx]; + if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { + return; + } - emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); - }, - } + emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); + }, + } }; } From d6dcfa5744034c178ea4174342618b915112e431 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 08:25:06 +0200 Subject: [PATCH 430/806] internal: Less file parsing for symbol index generation --- crates/hir-def/src/attr.rs | 2 +- crates/hir-expand/src/ast_id_map.rs | 8 +++--- crates/hir-expand/src/lib.rs | 1 + crates/hir/src/symbols.rs | 8 ++++++ crates/rust-analyzer/src/lsp_utils.rs | 35 ++++----------------------- 5 files changed, 19 insertions(+), 35 deletions(-) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index f93d8d5c78e85..de515b569dfa9 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -764,7 +764,7 @@ impl<'attr> AttrQuery<'attr> { .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text), _ => None } }) diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index e48caca790cf0..400442de94b98 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -124,6 +124,10 @@ impl AstIdMap { FileAstId { raw, _ty: PhantomData } } + pub fn get(&self, id: FileAstId) -> AstPtr { + AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() + } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); @@ -137,10 +141,6 @@ impl AstIdMap { } } - pub fn get(&self, id: FileAstId) -> AstPtr { - AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() - } - fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { self.arena.alloc(SyntaxNodePtr::new(item)) } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index c24887e15ff1a..c8373778d32b0 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -978,6 +978,7 @@ fn ascend_node_border_tokens( let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next); let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev); + // FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore let first = first_token(node)?; let last = last_token(node)?; let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?; diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 5d68aa52e62c7..4eaf99e3e73fa 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -39,11 +39,19 @@ impl DeclarationLocation { } pub fn original_range(&self, db: &dyn HirDatabase) -> FileRange { + if let Some(file_id) = self.hir_file_id.file_id() { + // fast path to prevent parsing + return FileRange { file_id, range: self.ptr.text_range() }; + } let node = resolve_node(db, self.hir_file_id, &self.ptr); node.as_ref().original_file_range(db.upcast()) } pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { + if let Some(file_id) = self.hir_file_id.file_id() { + // fast path to prevent parsing + return Some(FileRange { file_id, range: self.ptr.text_range() }); + } let node = resolve_node(db, self.hir_file_id, &self.name_ptr); node.as_ref().original_file_range_opt(db.upcast()) } diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 9a73da1539830..74e79e8e6050a 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -81,39 +81,14 @@ impl GlobalState { match additional_info { Some(additional_info) => { tracing::error!("{}:\n{}", &message, &additional_info); - match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) { - true => self.send_request::( - lsp_types::ShowMessageRequestParams { - typ: lsp_types::MessageType::ERROR, - message, - actions: Some(vec![lsp_types::MessageActionItem { - title: "Open server logs".to_owned(), - properties: Default::default(), - }]), - }, - |this, resp| { - let lsp_server::Response { error: None, result: Some(result), .. } = resp - else { return }; - if let Ok(Some(_item)) = crate::from_json::< - ::Result, - >( - lsp_types::request::ShowMessageRequest::METHOD, &result - ) { - this.send_notification::(()); - } - }, - ), - false => self.send_notification::( - lsp_types::ShowMessageParams { - typ: lsp_types::MessageType::ERROR, - message, - }, - ), - } + self.show_message( + lsp_types::MessageType::ERROR, + message, + tracing::enabled!(tracing::Level::ERROR), + ); } None => { tracing::error!("{}", &message); - self.send_notification::( lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message }, ); From 0028e73927fc4e3402c7243511936de0f2bc17bc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 May 2023 22:08:58 +0200 Subject: [PATCH 431/806] fix: Force disable augmentsSyntaxTokens capability on VSCode --- editors/code/src/client.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index b27d9f5494394..f721fcce76638 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -366,6 +366,7 @@ export async function createClient( // To turn on all proposed features use: client.registerProposedFeatures(); client.registerFeature(new ExperimentalFeatures()); + client.registerFeature(new OverrideFeatures()); return client; } @@ -401,6 +402,25 @@ class ExperimentalFeatures implements lc.StaticFeature { dispose(): void {} } +class OverrideFeatures implements lc.StaticFeature { + getState(): lc.FeatureState { + return { kind: "static" }; + } + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + // Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete + // making the experience generally worse + const caps = capabilities.textDocument?.semanticTokens; + if (caps) { + caps.augmentsSyntaxTokens = false; + } + } + initialize( + _capabilities: lc.ServerCapabilities, + _documentSelector: lc.DocumentSelector | undefined + ): void {} + dispose(): void {} +} + function isCodeActionWithoutEditsAndCommands(value: any): boolean { const candidate: lc.CodeAction = value; return ( From 03fb1310c24e266bc9dd05357b60a4d2b6e976df Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 08:39:54 +0200 Subject: [PATCH 432/806] fix: Fix bind pat hint padding --- crates/ide/src/inlay_hints/bind_pat.rs | 223 ++++++++++++------------- crates/ide/src/inlay_hints/chaining.rs | 4 +- 2 files changed, 105 insertions(+), 122 deletions(-) diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 758338e6d6a3a..07b9f9cc1fff6 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -89,8 +89,8 @@ pub(super) fn hints( None }; - let has_colon = matches!(type_ascriptable, Some(Some(_))) && !config.render_colons; - if !has_colon { + let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_))); + if render_colons { label.prepend_str(": "); } @@ -107,7 +107,7 @@ pub(super) fn hints( label, text_edit, position: InlayHintPosition::After, - pad_left: false, + pad_left: !render_colons, pad_right: false, }); @@ -194,7 +194,7 @@ mod tests { fn foo(a: i32, b: i32) -> i32 { a + b } fn main() { let _x = foo(4, 4); - //^^ : i32 + //^^ i32 }"#, ); } @@ -206,17 +206,17 @@ fn main() { //- minicore: option fn main() { let ref foo @ bar @ ref mut baz = 0; - //^^^ : &i32 - //^^^ : i32 - //^^^ : &mut i32 + //^^^ &i32 + //^^^ i32 + //^^^ &mut i32 let [x @ ..] = [0]; - //^ : [i32; 1] + //^ [i32; 1] if let x @ Some(_) = Some(0) {} - //^ : Option + //^ Option let foo @ (bar, baz) = (3, 3); - //^^^ : (i32, i32) - //^^^ : i32 - //^^^ : i32 + //^^^ (i32, i32) + //^^^ i32 + //^^^ i32 }"#, ); } @@ -229,11 +229,11 @@ struct Test { k: K, t: T } fn main() { let zz = Test { t: 23u8, k: 33 }; - //^^ : Test + //^^ Test let zz_ref = &zz; - //^^^^^^ : &Test + //^^^^^^ &Test let test = || zz; - //^^^^ : impl FnOnce() -> Test + //^^^^ impl FnOnce() -> Test }"#, ); } @@ -261,10 +261,10 @@ impl Iterator for SomeIter { fn main() { let mut some_iter = SomeIter::new(); - //^^^^^^^^^ : SomeIter>> + //^^^^^^^^^ SomeIter>> some_iter.push(iter::repeat(2).take(2)); let iter_of_iters = some_iter.take(2); - //^^^^^^^^^^^^^ : impl Iterator> + //^^^^^^^^^^^^^ impl Iterator> } "#, ); @@ -323,7 +323,7 @@ fn main(a: SliceIter<'_, Container>) { pub fn quux() -> T::Bar { let y = Default::default(); - //^ : ::Bar + //^ ::Bar y } @@ -347,21 +347,21 @@ fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } fn main() { let foo = foo(); - // ^^^ : impl Fn() + // ^^^ impl Fn() let foo = foo1(); - // ^^^ : impl Fn(f64) + // ^^^ impl Fn(f64) let foo = foo2(); - // ^^^ : impl Fn(f64, f64) + // ^^^ impl Fn(f64, f64) let foo = foo3(); - // ^^^ : impl Fn(f64, f64) -> u32 + // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ : &dyn Fn(f64, f64) -> u32 + // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ : &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); - // ^^^ : impl Fn(f64, f64) -> u32 + // ^^^ impl Fn(f64, f64) -> u32 let foo = foo7(); - // ^^^ : *const impl Fn(f64, f64) -> u32 + // ^^^ *const impl Fn(f64, f64) -> u32 } "#, ) @@ -384,9 +384,9 @@ fn main() { let foo = foo(); let foo = foo1(); let foo = foo2(); - // ^^^ : impl Fn(f64, f64) + // ^^^ impl Fn(f64, f64) let foo = foo3(); - // ^^^ : impl Fn(f64, f64) -> u32 + // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); let foo = foo5(); let foo = foo6(); @@ -427,25 +427,25 @@ fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} } fn main() { let foo = foo1(); - // ^^^ : *const impl Fn() + // ^^^ *const impl Fn() let foo = foo2(); - // ^^^ : *const impl Fn() + // ^^^ *const impl Fn() let foo = foo3(); - // ^^^ : *const (impl Fn() + ?Sized) + // ^^^ *const (impl Fn() + ?Sized) let foo = foo4(); - // ^^^ : *const impl Fn() + // ^^^ *const impl Fn() let foo = foo5(); - // ^^^ : *const (impl Fn() + ?Sized) + // ^^^ *const (impl Fn() + ?Sized) let foo = foo6(); - // ^^^ : *const (impl Fn() + Trait) + // ^^^ *const (impl Fn() + Trait) let foo = foo7(); - // ^^^ : *const (impl Fn() + Trait) + // ^^^ *const (impl Fn() + Trait) let foo = foo8(); - // ^^^ : *const (impl Fn() + Trait + ?Sized) + // ^^^ *const (impl Fn() + Trait + ?Sized) let foo = foo9(); - // ^^^ : *const (impl Fn() -> u8 + ?Sized) + // ^^^ *const (impl Fn() -> u8 + ?Sized) let foo = foo10(); - // ^^^ : *const impl Fn() + // ^^^ *const impl Fn() } "#, ) @@ -496,24 +496,24 @@ fn main() { struct InnerStruct {} let test = 54; - //^^^^ : i32 + //^^^^ i32 let test: i32 = 33; let mut test = 33; - //^^^^ : i32 + //^^^^ i32 let _ = 22; let test = "test"; - //^^^^ : &str + //^^^^ &str let test = InnerStruct {}; - //^^^^ : InnerStruct + //^^^^ InnerStruct let test = unresolved(); let test = (42, 'a'); - //^^^^ : (i32, char) - let (a, (b, (c,)) = (2, (3, (9.2,)); - //^ : i32 ^ : i32 ^ : f64 + //^^^^ (i32, char) + let (a, (b, (c,)) = (2, (3, (9.2,)); + //^ i32 ^ i32 ^ f64 let &x = &92; - //^ : i32 + //^ i32 }"#, ); } @@ -526,24 +526,7 @@ fn main() { struct Test { a: Option, b: u8 } fn main() { - let test = Some(Test { a: Some(3), b: 1 }); - //^^^^ : Option - if let None = &test {}; - if let test = &test {}; - //^^^^ : &Option - if let Some(test) = &test {}; - //^^^^ : &Test - if let Some(Test { a, b }) = &test {}; - //^ : &Option ^ : &u8 - if let Some(Test { a: x, b: y }) = &test {}; - //^ : &Option ^ : &u8 - if let Some(Test { a: Some(x), b: y }) = &test {}; - //^ : &u32 ^ : &u8 - if let Some(Test { a: None, b: y }) = &test {}; - //^ : &u8 - if let Some(Test { b: y, .. }) = &test {}; - //^ : &u8 - if test == None {} + }"#, ); } @@ -557,9 +540,9 @@ struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); - //^^^^ : Option + //^^^^ Option while let Some(Test { a: Some(x), b: y }) = &test {}; - //^ : &u32 ^ : &u8 + //^ &u32 ^ &u8 }"#, ); } @@ -575,9 +558,9 @@ fn main() { match Some(Test { a: Some(3), b: 1 }) { None => (), test => (), - //^^^^ : Option - Some(Test { a: Some(x), b: y }) => (), - //^ : u32 ^ : u8 + //^^^^ Option + Some(Test { a: Some(x), b: y }) => (), + //^ u32 ^ u8 _ => {} } }"#, @@ -609,12 +592,12 @@ impl Iterator for IntoIter { fn main() { let mut data = Vec::new(); - //^^^^ : Vec<&str> + //^^^^ Vec<&str> data.push("foo"); for i in data { - //^ : &str + //^ &str let z = i; - //^ : &str + //^ &str } } "#, @@ -639,11 +622,11 @@ auto trait Sync {} fn main() { // The block expression wrapping disables the constructor hint hiding logic let _v = { Vec::>::new() }; - //^^ : Vec> + //^^ Vec> let _v = { Vec::>::new() }; - //^^ : Vec> + //^^ Vec> let _v = { Vec::>::new() }; - //^^ : Vec> + //^^ Vec> } "#, ); @@ -667,14 +650,14 @@ impl Iterator for MyIter { fn main() { let _x = MyIter; - //^^ : MyIter + //^^ MyIter let _x = iter::repeat(0); - //^^ : impl Iterator + //^^ impl Iterator fn generic(t: T) { let _x = iter::repeat(t); - //^^ : impl Iterator + //^^ impl Iterator let _chained = iter::repeat(t).take(10); - //^^^^^^^^ : impl Iterator + //^^^^^^^^ impl Iterator } } "#, @@ -738,20 +721,20 @@ fn main() { let tuple_struct = TupleStruct(); let generic0 = Generic::new(); - // ^^^^^^^^ : Generic + // ^^^^^^^^ Generic let generic1 = Generic(0); - // ^^^^^^^^ : Generic + // ^^^^^^^^ Generic let generic2 = Generic::::new(); let generic3 = >::new(); let generic4 = Generic::(0); let option = Some(0); - // ^^^^^^ : Option + // ^^^^^^ Option let func = times2; - // ^^^^ : fn times2(i32) -> i32 + // ^^^^ fn times2(i32) -> i32 let closure = |x: i32| x * 2; - // ^^^^^^^ : impl Fn(i32) -> i32 + // ^^^^^^^ impl Fn(i32) -> i32 } fn fallible() -> ControlFlow<()> { @@ -789,20 +772,20 @@ impl Generic { fn main() { let strukt = Struct::new(); - // ^^^^^^ : Struct + // ^^^^^^ Struct let tuple_struct = TupleStruct(); - // ^^^^^^^^^^^^ : TupleStruct + // ^^^^^^^^^^^^ TupleStruct let generic0 = Generic::new(); - // ^^^^^^^^ : Generic + // ^^^^^^^^ Generic let generic1 = Generic::::new(); - // ^^^^^^^^ : Generic + // ^^^^^^^^ Generic let generic2 = >::new(); - // ^^^^^^^^ : Generic + // ^^^^^^^^ Generic } fn fallible() -> ControlFlow<()> { let strukt = Struct::try_new()?; - // ^^^^^^ : Struct + // ^^^^^^ Struct } "#, ); @@ -816,15 +799,15 @@ fn fallible() -> ControlFlow<()> { //- minicore: fn fn main() { let x = || 2; - //^ : impl Fn() -> i32 + //^ impl Fn() -> i32 let y = |t: i32| x() + t; - //^ : impl Fn(i32) -> i32 + //^ impl Fn(i32) -> i32 let mut t = 5; - //^ : i32 + //^ i32 let z = |k: i32| { t += k; }; - //^ : impl FnMut(i32) + //^ impl FnMut(i32) let p = (y, z); - //^ : (impl Fn(i32) -> i32, impl FnMut(i32)) + //^ (impl Fn(i32) -> i32, impl FnMut(i32)) } "#, ); @@ -838,15 +821,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ : || -> i32 + //^ || -> i32 let y = |t: i32| x() + t; - //^ : |i32| -> i32 + //^ |i32| -> i32 let mut t = 5; - //^ : i32 + //^ i32 let z = |k: i32| { t += k; }; - //^ : |i32| -> () + //^ |i32| -> () let p = (y, z); - //^ : (|i32| -> i32, |i32| -> ()) + //^ (|i32| -> i32, |i32| -> ()) } "#, ); @@ -860,15 +843,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ : {closure#0} + //^ {closure#0} let y = |t: i32| x() + t; - //^ : {closure#1} + //^ {closure#1} let mut t = 5; - //^ : i32 + //^ i32 let z = |k: i32| { t += k; }; - //^ : {closure#2} + //^ {closure#2} let p = (y, z); - //^ : ({closure#1}, {closure#2}) + //^ ({closure#1}, {closure#2}) } "#, ); @@ -882,15 +865,15 @@ fn main() { //- minicore: fn fn main() { let x = || 2; - //^ : … + //^ … let y = |t: i32| x() + t; - //^ : … + //^ … let mut t = 5; - //^ : i32 + //^ i32 let z = |k: i32| { t += k; }; - //^ : … + //^ … let p = (y, z); - //^ : (…, …) + //^ (…, …) } "#, ); @@ -910,24 +893,24 @@ fn main() { let multiple_2 = |x: i32| { x * 2 }; let multiple_2 = |x: i32| x * 2; - // ^^^^^^^^^^ : impl Fn(i32) -> i32 + // ^^^^^^^^^^ impl Fn(i32) -> i32 let (not) = (|x: bool| { !x }); - // ^^^ : impl Fn(bool) -> bool + // ^^^ impl Fn(bool) -> bool let (is_zero, _b) = (|x: usize| { x == 0 }, false); - // ^^^^^^^ : impl Fn(usize) -> bool - // ^^ : bool + // ^^^^^^^ impl Fn(usize) -> bool + // ^^ bool let plus_one = |x| { x + 1 }; - // ^ : u8 + // ^ u8 foo(plus_one); let add_mul = bar(|x: u8| { x + 1 }); - // ^^^^^^^ : impl FnOnce(u8) -> u8 + ?Sized + // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized let closure = if let Some(6) = add_mul(2).checked_sub(1) { - // ^^^^^^^ : fn(i32) -> i32 + // ^^^^^^^ fn(i32) -> i32 |x: i32| { x * 2 } } else { |x: i32| { x * 3 } @@ -954,11 +937,11 @@ struct VeryLongOuterName(T); fn main() { let a = Smol(0u32); - //^ : Smol + //^ Smol let b = VeryLongOuterName(0usize); - //^ : VeryLongOuterName<…> + //^ VeryLongOuterName<…> let c = Smol(Smol(0u32)) - //^ : Smol> + //^ Smol> }"#, ); } diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 5b7b02700338e..451c15a25c3bb 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -626,11 +626,11 @@ fn main() { InlayHint { range: 124..130, position: After, - pad_left: false, + pad_left: true, pad_right: false, kind: Type, label: [ - ": ", + "", InlayHintLabelPart { text: "Struct", linked_location: Some( From 261047d01932aac1b40e36076f88fc19f9fea218 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 11:29:03 +0330 Subject: [PATCH 433/806] Fix layout for `hir_ty::Ty` and friends --- crates/hir-ty/src/consteval/tests.rs | 30 ++++++++++ crates/hir-ty/src/db.rs | 7 ++- crates/hir-ty/src/display.rs | 10 ++-- crates/hir-ty/src/layout.rs | 2 +- crates/hir-ty/src/layout/adt.rs | 6 +- crates/hir-ty/src/layout/tests.rs | 56 +++++++++++++------ crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir/src/lib.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 4 +- 9 files changed, 89 insertions(+), 30 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 41e0b5188b38c..c7f358d4a4b12 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1962,6 +1962,36 @@ fn const_generic_subst_fn() { ); } +#[test] +fn layout_of_type_with_associated_type_field_defined_inside_body() { + check_number( + r#" +trait Tr { + type Ty; +} + +struct St(T::Ty); + +const GOAL: i64 = { + // if we move `St2` out of body, the test will fail, as we don't see the impl anymore. That + // case will probably be rejected by rustc in some later edition, but we should support this + // case. + struct St2; + + impl Tr for St2 { + type Ty = i64; + } + + struct Goal(St); + + let x = Goal(St(5)); + x.0.0 +}; +"#, + 5, + ); +} + #[test] fn const_generic_subst_assoc_const_impl() { check_number( diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index b37d90c75895a..0196d7b9e0c77 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -74,7 +74,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::layout_of_adt_query)] #[salsa::cycle(crate::layout::layout_of_adt_recover)] - fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result; + fn layout_of_adt( + &self, + def: AdtId, + subst: Substitution, + krate: CrateId, + ) -> Result; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 3cfe78141d43b..1d7f6cc0c15b1 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -463,6 +463,9 @@ fn render_const_scalar( memory_map: &MemoryMap, ty: &Ty, ) -> Result<(), HirDisplayError> { + // FIXME: We need to get krate from the final callers of the hir display + // infrastructure and have it here as a field on `f`. + let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); match ty.kind(Interner) { chalk_ir::TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), @@ -502,11 +505,6 @@ fn render_const_scalar( _ => f.write_str(""), }, chalk_ir::TyKind::Tuple(_, subst) => { - // FIXME: Remove this line. If the target data layout is independent - // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need - // to get krate. Otherwise, we need to get krate from the final callers of the hir display - // infrastructure and have it here as a field on `f`. - let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); let Ok(layout) = layout_of_ty(f.db, ty, krate) else { return f.write_str(""); }; @@ -532,7 +530,7 @@ fn render_const_scalar( chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { hir_def::AdtId::StructId(s) => { let data = f.db.struct_data(s); - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else { + let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { return f.write_str(""); }; match data.variant_data.as_ref() { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index f74a2253a424d..f67b49907cec2 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -84,7 +84,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result db.layout_of_adt(*def, subst.clone())?, + TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone(), krate)?, TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( dl, diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 81793e3795a64..da609a0957348 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -2,10 +2,11 @@ use std::{cmp, ops::Bound}; +use base_db::CrateId; use hir_def::{ data::adt::VariantData, layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, - AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, + AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; use smallvec::SmallVec; @@ -27,8 +28,8 @@ pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, + krate: CrateId, ) -> Result { - let krate = def.module(db.upcast()).krate(); let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = cx.current_data_layout(); @@ -127,6 +128,7 @@ pub fn layout_of_adt_recover( _: &[String], _: &AdtId, _: &Substitution, + _: &CrateId, ) -> Result { user_error!("infinite sized recursive type"); } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index e1038c0affe94..0b5d9df0c1d1b 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -25,22 +25,25 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result { "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}", ); - let (db, file_id) = TestDB::with_single_file(&ra_fixture); - let module_id = db.module_for_file(file_id); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id.local_id].scope; - let adt_id = scope - .declarations() - .find_map(|x| match x { - hir_def::ModuleDefId::AdtId(x) => { - let name = match x { - hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), - hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), - hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), - }; - (name == "Goal").then_some(x) - } - _ => None, + let (db, file_ids) = TestDB::with_many_files(&ra_fixture); + let (adt_id, module_id) = file_ids + .into_iter() + .find_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let adt_id = scope.declarations().find_map(|x| match x { + hir_def::ModuleDefId::AdtId(x) => { + let name = match x { + hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), + hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), + hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), + }; + (name == "Goal").then_some(x) + } + _ => None, + })?; + Some((adt_id, module_id)) }) .unwrap(); let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner); @@ -232,6 +235,27 @@ fn associated_types() { struct Foo(
::Ty); struct Goal(Foo); } + check_size_and_align( + r#" +//- /b/mod.rs crate:b +pub trait Tr { + type Ty; +} +pub struct Foo(::Ty); + +//- /a/mod.rs crate:a deps:b +use b::{Tr, Foo}; + +struct S; +impl Tr for S { + type Ty = i64; +} +struct Goal(Foo); + "#, + "", + 8, + 8, + ); } #[test] diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index c55ecb056c05a..f3dfab0dd1375 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -574,7 +574,7 @@ impl Evaluator<'_> { } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result { - self.db.layout_of_adt(adt, subst.clone()).map_err(|e| { + self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| { MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) }) } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d78d8f1e489a1..6b21a5f9c4ab6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1207,7 +1207,7 @@ impl Adt { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner)) + db.layout_of_adt(self.into(), Substitution::empty(Interner), self.krate(db).id) } /// Turns this ADT into a type. Any type parameters of the ADT will be diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index f7f49188662a7..673192f57163c 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -8,7 +8,7 @@ use std::{ use hir::{ db::{DefDatabase, ExpandDatabase, HirDatabase}, - AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef, + AssocItem, Crate, Function, HasCrate, HasSource, HirDisplay, ModuleDef, }; use hir_def::{ body::{BodySourceMap, SyntheticSyntax}, @@ -210,7 +210,7 @@ impl flags::AnalysisStats { continue; } all += 1; - let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner)) else { + let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into()) else { continue; }; if verbosity.is_spammy() { From 099b5b3b150a6cca3faffcf854d0ff798beed610 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 10:17:40 +0200 Subject: [PATCH 434/806] internal: Bump rustc_lexer --- Cargo.lock | 64 +++++++++++++++---- Cargo.toml | 2 + crates/parser/Cargo.toml | 2 +- crates/parser/src/lexed_str.rs | 56 ++++++++-------- crates/parser/src/syntax_kind/generated.rs | 3 +- .../err/unclosed_raw_byte_string_at_eof.rast | 2 +- ...sed_raw_byte_string_with_ascii_escape.rast | 2 +- .../unclosed_raw_byte_string_with_ferris.rast | 2 +- .../unclosed_raw_byte_string_with_slash.rast | 2 +- ...unclosed_raw_byte_string_with_slash_n.rast | 2 +- .../unclosed_raw_byte_string_with_space.rast | 2 +- ...d_raw_byte_string_with_unicode_escape.rast | 2 +- .../lexer/err/unclosed_raw_string_at_eof.rast | 2 +- ...unclosed_raw_string_with_ascii_escape.rast | 2 +- .../err/unclosed_raw_string_with_ferris.rast | 2 +- .../err/unclosed_raw_string_with_slash.rast | 2 +- .../err/unclosed_raw_string_with_slash_n.rast | 2 +- .../err/unclosed_raw_string_with_space.rast | 2 +- ...closed_raw_string_with_unicode_escape.rast | 2 +- .../err/unstarted_raw_byte_string_at_eof.rast | 2 +- .../unstarted_raw_byte_string_with_ascii.rast | 2 +- .../err/unstarted_raw_string_at_eof.rast | 2 +- .../err/unstarted_raw_string_with_ascii.rast | 2 +- crates/syntax/Cargo.toml | 3 +- crates/syntax/src/tests/ast_src.rs | 2 +- crates/syntax/src/validation.rs | 39 +++++++---- 26 files changed, 134 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b22a58e568ac..7980823a854fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1193,7 +1193,7 @@ dependencies = [ "drop_bomb", "expect-test", "limit", - "rustc-ap-rustc_lexer", + "ra-ap-rustc_lexer", "sourcegen", "stdx", ] @@ -1396,6 +1396,16 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ra-ap-rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3" +dependencies = [ + "unic-emoji-char", + "unicode-xid", +] + [[package]] name = "rayon" version = "1.7.0" @@ -1524,15 +1534,6 @@ dependencies = [ "xshell", ] -[[package]] -name = "rustc-ap-rustc_lexer" -version = "727.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f40f26e7abdcd3b982f36c09a634cc6187988fbf6ec466c91f8d30a12ac0237" -dependencies = [ - "unicode-xid", -] - [[package]] name = "rustc-demangle" version = "0.1.22" @@ -1753,9 +1754,9 @@ dependencies = [ "proc-macro2", "profile", "quote", + "ra-ap-rustc_lexer", "rayon", "rowan", - "rustc-ap-rustc_lexer", "rustc-hash", "smol_str", "sourcegen", @@ -1998,6 +1999,47 @@ version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.6.0" diff --git a/Cargo.toml b/Cargo.toml index c7b0228e331c3..513a769ca177d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,3 +89,5 @@ text-size = "1.1.0" serde = { version = "=1.0.156", features = ["derive"] } serde_json = "1.0.94" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } + +rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 6e962abd75477..09e62c35278e9 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] drop_bomb = "0.1.5" -rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } +rustc_lexer.workspace = true limit.workspace = true diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs index 100deff462d25..ace6985a583b2 100644 --- a/crates/parser/src/lexed_str.rs +++ b/crates/parser/src/lexed_str.rs @@ -36,7 +36,7 @@ impl<'a> LexedStr<'a> { }; for token in rustc_lexer::tokenize(&text[conv.offset..]) { - let token_text = &text[conv.offset..][..token.len]; + let token_text = &text[conv.offset..][..token.len as usize]; conv.extend_token(&token.kind, token_text); } @@ -49,8 +49,8 @@ impl<'a> LexedStr<'a> { return None; } - let token = rustc_lexer::first_token(text); - if token.len != text.len() { + let token = rustc_lexer::tokenize(text).next()?; + if token.len as usize != text.len() { return None; } @@ -175,6 +175,10 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Ident => { SyntaxKind::from_keyword(token_text).unwrap_or(IDENT) } + rustc_lexer::TokenKind::InvalidIdent => { + err = "Ident contains invalid characters"; + IDENT + } rustc_lexer::TokenKind::RawIdent => IDENT, rustc_lexer::TokenKind::Literal { kind, .. } => { @@ -221,6 +225,7 @@ impl<'a> Converter<'a> { err = "unknown literal prefix"; IDENT } + rustc_lexer::TokenKind::Eof => EOF, } }; @@ -268,35 +273,30 @@ impl<'a> Converter<'a> { } BYTE_STRING } - rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => { - if let Some(raw_str_err) = raw_str_err { - err = match raw_str_err { - rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal", - rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found { - "Missing trailing `\"` to terminate the raw string literal" - } else { - "Missing trailing `\"` with `#` symbols to terminate the raw string literal" - }, - rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols", - }; - }; + rustc_lexer::LiteralKind::CStr { terminated } => { + if !terminated { + err = "Missing trailing `\"` symbol to terminate the string literal"; + } STRING } - rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => { - if let Some(raw_str_err) = raw_str_err { - err = match raw_str_err { - rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal", - rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found { - "Missing trailing `\"` to terminate the raw byte string literal" - } else { - "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal" - }, - rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols", - }; - }; - + rustc_lexer::LiteralKind::RawStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } + STRING + } + rustc_lexer::LiteralKind::RawByteStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } BYTE_STRING } + rustc_lexer::LiteralKind::RawCStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } + STRING + } }; let err = if err.is_empty() { None } else { Some(err) }; diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 2af6e1b9867cf..a8fbcfacf7ece 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -117,6 +117,7 @@ pub enum SyntaxKind { BYTE, STRING, BYTE_STRING, + C_STRING, ERROR, IDENT, WHITESPACE, @@ -379,7 +380,7 @@ impl SyntaxKind { ) } pub fn is_literal(self) -> bool { - matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING) + matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING) } pub fn from_keyword(ident: &str) -> Option { let kw = match ident { diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast index 6ec1780c30b85..cab02d38af2ed 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast @@ -1 +1 @@ -BYTE_STRING "br##\"" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast index d65f1bb2ff049..0486a1e8e1d23 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\x7f" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast index 0f9e0a1657ad4..41e3455c1f3f8 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast @@ -1 +1 @@ -BYTE_STRING "br##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"🦀" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast index 202dcd2d43e38..a11208a81fe8d 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast index d45485b529ed6..10a47ab844757 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\n" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast index 1bfabbc3ab622..b41ea3a170155 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast @@ -1 +1 @@ -BYTE_STRING "br##\" " error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\" " error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast index 104ab8aaeefa4..63b8a5af80907 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\u{20AA}" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast index 71b20fd19db7c..096bb94031527 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast @@ -1 +1 @@ -STRING "r##\"" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast index dc106dd24a111..f0ad200fea5a5 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast @@ -1 +1 @@ -STRING "r##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\x7f" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast index 30ee029f65679..bc5996d1e67ba 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast @@ -1 +1 @@ -STRING "r##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"🦀" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast index 8a6f6cc436662..b48ec5ddabbe5 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast @@ -1 +1 @@ -STRING "r##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast index f46eff2516ac4..9f32f677766c4 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast @@ -1 +1 @@ -STRING "r##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\n" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast index 49b6afea45a5c..2804a43cf1fee 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast @@ -1 +1 @@ -STRING "r##\" " error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\" " error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast index d10d6d8e8c2fd..eb0a2d2da10af 100644 --- a/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast +++ b/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast @@ -1 +1 @@ -STRING "r##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\u{20AA}" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast b/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast index cf942c92f3b19..52a7f03b6f559 100644 --- a/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast +++ b/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast @@ -1 +1 @@ -BYTE_STRING "br##" error: Missing `"` symbol after `#` symbols to begin the raw byte string literal +BYTE_STRING "br##" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast b/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast index 042769c275642..da5550d4cc186 100644 --- a/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast +++ b/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast @@ -1,4 +1,4 @@ -BYTE_STRING "br## " error: Missing `"` symbol after `#` symbols to begin the raw byte string literal +BYTE_STRING "br## " error: Invalid raw string literal IDENT "I" WHITESPACE " " IDENT "lack" diff --git a/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast b/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast index 2f7c7529a95c0..50b962e77a480 100644 --- a/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast +++ b/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast @@ -1 +1 @@ -STRING "r##" error: Missing `"` symbol after `#` symbols to begin the raw string literal +STRING "r##" error: Invalid raw string literal diff --git a/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast b/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast index 4a06b0abe748e..1f484299af156 100644 --- a/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast +++ b/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast @@ -1,4 +1,4 @@ -STRING "r## " error: Missing `"` symbol after `#` symbols to begin the raw string literal +STRING "r## " error: Invalid raw string literal IDENT "I" WHITESPACE " " IDENT "lack" diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index ac5b3058b97a1..fb38d25ab541e 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -17,13 +17,14 @@ cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" rowan = "0.15.11" -rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } rustc-hash = "1.1.0" once_cell = "1.17.0" indexmap = "1.9.1" smol_str.workspace = true triomphe.workspace = true +rustc_lexer.workspace = true + parser.workspace = true profile.workspace = true stdx.workspace = true diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs index caef6a7953971..c5783b91a0fdb 100644 --- a/crates/syntax/src/tests/ast_src.rs +++ b/crates/syntax/src/tests/ast_src.rs @@ -71,7 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield", ], contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"], - literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"], + literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"], tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"], nodes: &[ "SOURCE_FILE", diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 5ff01eb44e719..089ad74dadc13 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -5,7 +5,7 @@ mod block; use rowan::Direction; -use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, @@ -44,7 +44,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec { errors } -fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { +fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) { use unescape::EscapeError as EE; #[rustfmt::skip] @@ -103,12 +103,15 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { EE::UnicodeEscapeInByte => { "Byte literals must not contain unicode escapes" } - EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => { + EE::NonAsciiCharInByte => { "Byte literals must not contain non-ASCII characters" } + EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped", + EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape", + }; - err_message + (err_message, err.is_fatal()) } fn validate_literal(literal: ast::Literal, acc: &mut Vec) { @@ -121,9 +124,13 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { let text = token.text(); // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205) - let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { + let mut push_err = |prefix_len, off, err: unescape::EscapeError| { let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap(); - acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off)); + let (message, is_err) = rustc_unescape_error_to_string(err); + // FIXME: Emit lexer warnings + if is_err { + acc.push(SyntaxError::new_at_offset(message, off)); + } }; match literal.kind() { @@ -132,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { if let Some(without_quotes) = unquote(text, 1, '"') { unescape_literal(without_quotes, Mode::Str, &mut |range, char| { if let Err(err) = char { - push_err(1, (range.start, err)); + push_err(1, range.start, err); } }); } @@ -143,20 +150,28 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { if let Some(without_quotes) = unquote(text, 2, '"') { unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { if let Err(err) = char { - push_err(2, (range.start, err)); + push_err(1, range.start, err); } }); } } } ast::LiteralKind::Char(_) => { - if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) { - push_err(1, e); + if let Some(without_quotes) = unquote(text, 1, '\'') { + unescape_literal(without_quotes, Mode::Char, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::Byte(_) => { - if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) { - push_err(2, e); + if let Some(without_quotes) = unquote(text, 2, '\'') { + unescape_literal(without_quotes, Mode::Byte, &mut |range, char| { + if let Err(err) = char { + push_err(2, range.start, err); + } + }); } } ast::LiteralKind::IntNumber(_) From 5c83e222a3e001488a313e84a3e584ac0c773d8a Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 12:32:41 +0330 Subject: [PATCH 435/806] fix `format_args` expansion error with raw strings --- .../macro_expansion_tests/builtin_fn_macro.rs | 33 +++++++++++++++++++ crates/hir-expand/src/builtin_fn_macro.rs | 26 ++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 0beab57eb738a..f5346898c2029 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -235,6 +235,39 @@ fn main() { ); } +#[test] +fn test_format_args_expand_with_raw_strings() { + check( + r##" +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + format_args!( + r#"{},mismatch,"{}","{}""#, + location_csv_pat(db, &analysis, vfs, &sm, pat_id), + mismatch.expected.display(db), + mismatch.actual.display(db) + ); +} +"##, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + $crate::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[$crate::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), $crate::fmt::Display::fmt), ]); +} +"##]], + ); +} + #[test] fn test_format_args_expand_eager() { check( diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 3f9ea96545ce2..e9e1c6c3b3340 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -287,16 +287,27 @@ fn format_args_expand_general( match token_tree { tt::TokenTree::Leaf(l) => match l { tt::Leaf::Literal(l) => { - let text = l.text.strip_prefix('"')?.strip_suffix('"')?; - let span = l.span; - Some((text, span)) + if let Some(mut text) = l.text.strip_prefix('r') { + let mut raw_sharps = String::new(); + while let Some(t) = text.strip_prefix('#') { + text = t; + raw_sharps.push('#'); + } + text = + text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?; + Some((text, l.span, Some(raw_sharps))) + } else { + let text = l.text.strip_prefix('"')?.strip_suffix('"')?; + let span = l.span; + Some((text, span, None)) + } } _ => None, }, tt::TokenTree::Subtree(_) => None, } })(); - let Some((format_string, _format_string_span)) = format_string else { + let Some((format_string, _format_string_span, raw_sharps)) = format_string else { return expand_error; }; let mut format_iter = format_string.chars().peekable(); @@ -379,7 +390,12 @@ fn format_args_expand_general( parts.push(last_part); } let part_tts = parts.into_iter().map(|x| { - let l = tt::Literal { span: tt::TokenId::unspecified(), text: format!("\"{}\"", x).into() }; + let text = if let Some(raw) = &raw_sharps { + format!("r{raw}\"{}\"{raw}", x).into() + } else { + format!("\"{}\"", x).into() + }; + let l = tt::Literal { span: tt::TokenId::unspecified(), text }; quote!(#l ,) }); let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); From 4b577e2bc847a791d87634b011856cd835d75947 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 11:06:05 +0200 Subject: [PATCH 436/806] Support c string literals --- crates/hir-def/src/body/pretty.rs | 1 + crates/hir-def/src/hir.rs | 5 ++ crates/hir-ty/src/infer/expr.rs | 2 +- crates/hir-ty/src/infer/pat.rs | 7 +- crates/hir-ty/src/mir/lower.rs | 13 +++- crates/ide-assists/src/handlers/raw_string.rs | 1 + crates/ide/src/extend_selection.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 17 ++++- .../ide/src/syntax_highlighting/highlight.rs | 2 +- crates/ide/src/syntax_tree.rs | 6 +- crates/parser/src/grammar/expressions/atom.rs | 3 + crates/parser/src/grammar/generic_args.rs | 1 + crates/parser/src/lexed_str.rs | 4 +- .../parser/inline/ok/0085_expr_literals.rast | 24 +++++++ .../parser/inline/ok/0085_expr_literals.rs | 2 + crates/syntax/src/ast/expr_ext.rs | 4 ++ crates/syntax/src/ast/generated/tokens.rs | 21 ++++++ crates/syntax/src/ast/token_ext.rs | 71 +++++++++++++++---- crates/syntax/src/parsing/reparsing.rs | 2 +- crates/syntax/src/tests/sourcegen_ast.rs | 9 +-- crates/syntax/src/validation.rs | 13 +++- 21 files changed, 176 insertions(+), 34 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index c3bd99b94876a..8f1e6c3bbdccd 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -611,6 +611,7 @@ impl<'a> Printer<'a> { match literal { Literal::String(it) => w!(self, "{:?}", it), Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), + Literal::CString(it) => w!(self, "\"{}\\0\"", it), Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), Literal::Bool(it) => w!(self, "{}", it), Literal::Int(i, suffix) => { diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 1f4577629ccdc..27150d8623cd7 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -85,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper { pub enum Literal { String(Box), ByteString(Box<[u8]>), + CString(Box), Char(char), Bool(bool), Int(i128, Option), @@ -135,6 +136,10 @@ impl From for Literal { let text = s.value().map(Box::from).unwrap_or_else(Default::default); Literal::String(text) } + LiteralKind::CString(s) => { + let text = s.value().map(Box::from).unwrap_or_else(Default::default); + Literal::CString(text) + } LiteralKind::Byte(b) => { Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8)) } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 353bf6568c107..385766fdad0bf 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -815,7 +815,7 @@ impl<'a> InferenceContext<'a> { Expr::Array(array) => self.infer_expr_array(array, expected), Expr::Literal(lit) => match lit { Literal::Bool(..) => self.result.standard_types.bool_.clone(), - Literal::String(..) => { + Literal::String(..) | Literal::CString(..) => { TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner)) .intern(Interner) } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 5748039511067..18b05fcf3c7ef 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -428,9 +428,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented. Pat::Path(..) => true, Pat::ConstBlock(..) => true, - Pat::Lit(expr) => { - !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..))) - } + Pat::Lit(expr) => !matches!( + body[*expr], + Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) + ), Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, } } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 627c36dca92f0..051ae228e22f9 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1112,15 +1112,24 @@ impl<'ctx> MirLowerCtx<'ctx> { let bytes = match l { hir_def::hir::Literal::String(b) => { let b = b.as_bytes(); - let mut data = vec![]; + let mut data = Vec::with_capacity(mem::size_of::() * 2); 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::hir::Literal::CString(b) => { + let b = b.as_bytes(); + let mut data = Vec::with_capacity(mem::size_of::() * 2); + data.extend(0usize.to_le_bytes()); + data.extend(b.len().to_le_bytes()); + let mut mm = MemoryMap::default(); + mm.insert(0, b.iter().copied().chain(iter::once(0)).collect::>()); + return Ok(Operand::from_concrete_const(data, mm, ty)); + } hir_def::hir::Literal::ByteString(b) => { - let mut data = vec![]; + let mut data = Vec::with_capacity(mem::size_of::() * 2); data.extend(0usize.to_le_bytes()); data.extend(b.len().to_le_bytes()); let mut mm = MemoryMap::default(); diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index 01420430bb419..40ee4771d1707 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs @@ -20,6 +20,7 @@ use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists // } // ``` pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // FIXME: This should support byte and c strings as well. let token = ctx.find_token_at_offset::()?; if token.is_raw() { return None; diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 9f78c75e90aa2..f906182224468 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs @@ -39,7 +39,7 @@ fn try_extend_selection( ) -> Option { let range = frange.range; - let string_kinds = [COMMENT, STRING, BYTE_STRING]; + let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING]; let list_kinds = [ RECORD_PAT_FIELD_LIST, MATCH_ARM_LIST, diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 751e51da0d289..8c02fe81648a5 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -16,7 +16,10 @@ mod tests; use hir::{Name, Semantics}; use ide_db::{FxHashMap, RootDatabase, SymbolKind}; use syntax::{ - ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T, + ast::{self, IsString}, + AstNode, AstToken, NodeOrToken, + SyntaxKind::*, + SyntaxNode, TextRange, WalkEvent, T, }; use crate::{ @@ -440,7 +443,17 @@ fn traverse( && ast::ByteString::can_cast(descended_token.kind()) { if let Some(byte_string) = ast::ByteString::cast(token) { - highlight_escape_string(hl, &byte_string, range.start()); + if !byte_string.is_raw() { + highlight_escape_string(hl, &byte_string, range.start()); + } + } + } else if ast::CString::can_cast(token.kind()) + && ast::CString::can_cast(descended_token.kind()) + { + if let Some(c_string) = ast::CString::cast(token) { + if !c_string.is_raw() { + highlight_escape_string(hl, &c_string, range.start()); + } } } else if ast::Char::can_cast(token.kind()) && ast::Char::can_cast(descended_token.kind()) diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 936362914aec0..925057ffaa090 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -26,7 +26,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O } let highlight: Highlight = match token.kind() { - STRING | BYTE_STRING => HlTag::StringLiteral.into(), + STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(), INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => { SymbolKind::Field.into() } diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs index bb6827e8a44e4..df1971242657d 100644 --- a/crates/ide/src/syntax_tree.rs +++ b/crates/ide/src/syntax_tree.rs @@ -1,5 +1,7 @@ -use ide_db::base_db::{FileId, SourceDatabase}; -use ide_db::RootDatabase; +use ide_db::{ + base_db::{FileId, SourceDatabase}, + RootDatabase, +}; use syntax::{ AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize, }; diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index d051dd2682f94..3cf9c4dd4b07a 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -12,6 +12,8 @@ use super::*; // let _ = r"d"; // let _ = b"e"; // let _ = br"f"; +// let _ = c"g"; +// let _ = cr"h"; // } pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ T![true], @@ -22,6 +24,7 @@ pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ CHAR, STRING, BYTE_STRING, + C_STRING, ]); pub(crate) fn literal(p: &mut Parser<'_>) -> Option { diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs index b7d72b8d33659..e589b69934d23 100644 --- a/crates/parser/src/grammar/generic_args.rs +++ b/crates/parser/src/grammar/generic_args.rs @@ -28,6 +28,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[ BYTE, STRING, BYTE_STRING, + C_STRING, ]) .union(types::TYPE_FIRST); diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs index ace6985a583b2..e4dce21f32abe 100644 --- a/crates/parser/src/lexed_str.rs +++ b/crates/parser/src/lexed_str.rs @@ -277,7 +277,7 @@ impl<'a> Converter<'a> { if !terminated { err = "Missing trailing `\"` symbol to terminate the string literal"; } - STRING + C_STRING } rustc_lexer::LiteralKind::RawStr { n_hashes } => { if n_hashes.is_none() { @@ -295,7 +295,7 @@ impl<'a> Converter<'a> { if n_hashes.is_none() { err = "Invalid raw string literal"; } - STRING + C_STRING } }; diff --git a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast index 403c265ea35be..fe73d9dfe4a75 100644 --- a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast +++ b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast @@ -131,6 +131,30 @@ SOURCE_FILE LITERAL BYTE_STRING "br\"f\"" SEMICOLON ";" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + C_STRING "c\"g\"" + SEMICOLON ";" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + C_STRING "cr\"h\"" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs index 2e11a5a6e68c8..e7f235a83b92c 100644 --- a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs +++ b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs @@ -9,4 +9,6 @@ fn foo() { let _ = r"d"; let _ = b"e"; let _ = br"f"; + let _ = c"g"; + let _ = cr"h"; } diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index c43d0830b9e24..1eef28611652f 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -288,6 +288,7 @@ impl ast::ArrayExpr { pub enum LiteralKind { String(ast::String), ByteString(ast::ByteString), + CString(ast::CString), IntNumber(ast::IntNumber), FloatNumber(ast::FloatNumber), Char(ast::Char), @@ -319,6 +320,9 @@ impl ast::Literal { if let Some(t) = ast::ByteString::cast(token.clone()) { return LiteralKind::ByteString(t); } + if let Some(t) = ast::CString::cast(token.clone()) { + return LiteralKind::CString(t); + } if let Some(t) = ast::Char::cast(token.clone()) { return LiteralKind::Char(t); } diff --git a/crates/syntax/src/ast/generated/tokens.rs b/crates/syntax/src/ast/generated/tokens.rs index a3209c5abd23e..f5863e9efe0da 100644 --- a/crates/syntax/src/ast/generated/tokens.rs +++ b/crates/syntax/src/ast/generated/tokens.rs @@ -90,6 +90,27 @@ impl AstToken for ByteString { fn syntax(&self) -> &SyntaxToken { &self.syntax } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CString { + pub(crate) syntax: SyntaxToken, +} +impl std::fmt::Display for CString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.syntax, f) + } +} +impl AstToken for CString { + fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING } + fn cast(syntax: SyntaxToken) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxToken { &self.syntax } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IntNumber { pub(crate) syntax: SyntaxToken, diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 2cd312e7f4f8c..090eb89f47040 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -145,6 +145,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { + const RAW_PREFIX: &'static str; + fn is_raw(&self) -> bool { + self.text().starts_with(Self::RAW_PREFIX) + } fn quote_offsets(&self) -> Option { let text = self.text(); let offsets = QuoteOffsets::new(text)?; @@ -183,20 +187,18 @@ pub trait IsString: AstToken { cb(text_range + offset, unescaped_char); }); } -} - -impl IsString for ast::String {} - -impl ast::String { - pub fn is_raw(&self) -> bool { - self.text().starts_with('r') - } - pub fn map_range_up(&self, range: TextRange) -> Option { + fn map_range_up(&self, range: TextRange) -> Option { let contents_range = self.text_range_between_quotes()?; assert!(TextRange::up_to(contents_range.len()).contains_range(range)); Some(range + contents_range.start()) } +} +impl IsString for ast::String { + const RAW_PREFIX: &'static str = "r"; +} + +impl ast::String { pub fn value(&self) -> Option> { if self.is_raw() { let text = self.text(); @@ -235,13 +237,11 @@ impl ast::String { } } -impl IsString for ast::ByteString {} +impl IsString for ast::ByteString { + const RAW_PREFIX: &'static str = "br"; +} impl ast::ByteString { - pub fn is_raw(&self) -> bool { - self.text().starts_with("br") - } - pub fn value(&self) -> Option> { if self.is_raw() { let text = self.text(); @@ -280,6 +280,49 @@ impl ast::ByteString { } } +impl IsString for ast::CString { + const RAW_PREFIX: &'static str = "cr"; +} + +impl ast::CString { + pub fn value(&self) -> Option> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text)); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf = String::new(); + let mut prev_end = 0; + let mut has_error = false; + unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( + unescaped_char, + buf.capacity() == 0, + ) { + (Ok(c), false) => buf.push(c), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.push_str(&text[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text)), + (false, false) => Some(Cow::Owned(buf)), + } + } +} + impl ast::IntNumber { pub fn radix(&self) -> Radix { match self.text().get(..2).unwrap_or_default() { diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs index f3d644c680e2e..45e5916098282 100644 --- a/crates/syntax/src/parsing/reparsing.rs +++ b/crates/syntax/src/parsing/reparsing.rs @@ -39,7 +39,7 @@ fn reparse_token( let prev_token = root.covering_element(edit.delete).as_token()?.clone(); let prev_token_kind = prev_token.kind(); match prev_token_kind { - WHITESPACE | COMMENT | IDENT | STRING => { + WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => { if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT { // removing a new line may extends previous token let deleted_range = edit.delete - prev_token.text_range().start(); diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index 77a8363a185c1..c49c5fa108b62 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -573,10 +573,11 @@ impl Field { fn lower(grammar: &Grammar) -> AstSrc { let mut res = AstSrc { - tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident" - .split_ascii_whitespace() - .map(|it| it.to_string()) - .collect::>(), + tokens: + "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident" + .split_ascii_whitespace() + .map(|it| it.to_string()) + .collect::>(), ..Default::default() }; diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 089ad74dadc13..e0ec6a242ffa7 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -9,7 +9,7 @@ use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, - ast::{self, HasAttrs, HasVisibility}, + ast::{self, HasAttrs, HasVisibility, IsString}, match_ast, AstNode, SyntaxError, SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS}, SyntaxNode, SyntaxToken, TextSize, T, @@ -156,6 +156,17 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { } } } + ast::LiteralKind::CString(s) => { + if !s.is_raw() { + if let Some(without_quotes) = unquote(text, 2, '"') { + unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); + } + } + } ast::LiteralKind::Char(_) => { if let Some(without_quotes) = unquote(text, 1, '\'') { unescape_literal(without_quotes, Mode::Char, &mut |range, char| { From ef38662d044714f55c87a12571bd30da51c199aa Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 18 May 2023 10:33:35 +0200 Subject: [PATCH 437/806] Some improvements to the manual_let_else lint suggestions * Replace variables inside | patterns in the if let: let v = if let V::A(v) | V::B(v) = v { v } else ... * Support nested patterns: let v = if let Ok(Ok(Ok(v))) = v { v } else ... * Support tuple structs with more than one arg: let v = V::W(v, _) = v { v } else ... * Correctly handle .. in tuple struct patterns: let v = V::X(v, ..) = v { v } else ... --- clippy_lints/src/manual_let_else.rs | 71 ++++++++++++++++++++------- tests/ui/manual_let_else.rs | 16 ++++-- tests/ui/manual_let_else.stderr | 35 ++++++++++--- tests/ui/manual_let_else_match.rs | 2 +- tests/ui/manual_let_else_match.stderr | 4 +- 5 files changed, 97 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 855bbf4c668b8..389b0a4a62dc5 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -146,10 +146,9 @@ fn emit_manual_let_else( "this could be rewritten as `let...else`", |diag| { // This is far from perfect, for example there needs to be: - // * mut additions for the bindings - // * renamings of the bindings for `PatKind::Or` + // * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ... + // * renamings of the bindings for many `PatKind`s like structs, slices, etc. // * unused binding collision detection with existing ones - // * putting patterns with at the top level | inside () // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); @@ -160,28 +159,62 @@ fn emit_manual_let_else( } else { format!("{{ {sn_else} }}") }; - let sn_bl = match pat.kind { - PatKind::Or(..) => { - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); - format!("({sn_pat})") - }, - // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. - PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => { - let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); - let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app); - format!("{sn_wrapper}({sn_inner})") - }, - _ => { - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); - sn_pat.into_owned() - }, - }; + let sn_bl = replace_in_pattern(cx, span, local, pat, &mut app); let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); }, ); } +// replaces the locals in the pattern +fn replace_in_pattern( + cx: &LateContext<'_>, + span: Span, + local: &Pat<'_>, + pat: &Pat<'_>, + app: &mut Applicability, +) -> String { + let mut bindings_count = 0; + pat.each_binding_or_first(&mut |_, _, _, _| bindings_count += 1); + // If the pattern creates multiple bindings, exit early, + // as otherwise we might paste the pattern to the positions of multiple bindings. + if bindings_count > 1 { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app); + return sn_pat.into_owned(); + } + + match pat.kind { + PatKind::Binding(..) => { + let (sn_bdg, _) = snippet_with_context(cx, local.span, span.ctxt(), "", app); + return sn_bdg.to_string(); + }, + PatKind::Or(pats) => { + let patterns = pats + .iter() + .map(|pat| replace_in_pattern(cx, span, local, pat, app)) + .collect::>(); + let or_pat = patterns.join(" | "); + return format!("({or_pat})"); + }, + // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. + PatKind::TupleStruct(ref w, args, dot_dot_pos) => { + let mut args = args + .iter() + .map(|pat| replace_in_pattern(cx, span, local, pat, app)) + .collect::>(); + if let Some(pos) = dot_dot_pos.as_opt_usize() { + args.insert(pos, "..".to_owned()); + } + let args = args.join(", "); + let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); + return format!("{sn_wrapper}({args})"); + }, + _ => {}, + } + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app); + sn_pat.into_owned() +} + /// Check whether an expression is divergent. May give false negatives. fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { struct V<'cx, 'tcx> { diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 3996d775f55f4..351ea0e4f509e 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -146,10 +146,20 @@ fn fire() { Variant::A(0, 0) } - // Should not be renamed let v = if let Variant::A(a, 0) = e() { a } else { return }; - // Should be renamed - let v = if let Variant::B(b) = e() { b } else { return }; + + // `mut v` is inserted into the pattern + let mut v = if let Variant::B(b) = e() { b } else { return }; + + // Nesting works + let nested = Ok(Some(e())); + let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { + b + } else { + return; + }; + // dot dot works + let v = if let Variant::A(.., a) = e() { a } else { return }; } fn not_fire() { diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index f6f56f7b00e51..0e87679713474 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -260,19 +260,42 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:150:5 + --> $DIR/manual_let_else.rs:149:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else.rs:152:5 | -LL | let v = if let Variant::B(b) = e() { b } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };` +LL | let mut v = if let Variant::B(b) = e() { b } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:262:5 + --> $DIR/manual_let_else.rs:156:5 + | +LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { +LL | | b +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let (Ok(Some(Variant::B(v))) | Err(Some(Variant::A(v, _)))) = nested else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:162:5 + | +LL | let v = if let Variant::A(.., a) = e() { a } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:272:5 | LL | / let _ = match ff { LL | | Some(value) => value, @@ -280,5 +303,5 @@ LL | | _ => macro_call!(), LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 73b7467912597..dfca3b023cd58 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -68,7 +68,7 @@ fn fire() { let f = Variant::Bar(1); let _value = match f { - Variant::Bar(_) | Variant::Baz(_) => (), + Variant::Bar(v) | Variant::Baz(v) => v, _ => return, }; diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index bacc14dc96720..13ed35bc1d5db 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -58,10 +58,10 @@ error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:70:5 | LL | / let _value = match f { -LL | | Variant::Bar(_) | Variant::Baz(_) => (), +LL | | Variant::Bar(v) | Variant::Baz(v) => v, LL | | _ => return, LL | | }; - | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` + | |______^ help: consider writing: `let (Variant::Bar(_value) | Variant::Baz(_value)) = f else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:76:5 From eab295cc73a876eeaa1424b3ed7a76bcf46a5fec Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 11:29:19 +0200 Subject: [PATCH 438/806] Fix mir CString lowering not respecting the extra 0 byte for length calc --- crates/hir-ty/src/mir/lower.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 051ae228e22f9..28305bbe81c8d 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1121,11 +1121,13 @@ impl<'ctx> MirLowerCtx<'ctx> { } hir_def::hir::Literal::CString(b) => { let b = b.as_bytes(); + let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); + let mut data = Vec::with_capacity(mem::size_of::() * 2); data.extend(0usize.to_le_bytes()); - data.extend(b.len().to_le_bytes()); + data.extend(bytes.len().to_le_bytes()); let mut mm = MemoryMap::default(); - mm.insert(0, b.iter().copied().chain(iter::once(0)).collect::>()); + mm.insert(0, bytes); return Ok(Operand::from_concrete_const(data, mm, ty)); } hir_def::hir::Literal::ByteString(b) => { From 3e528b85f9a3ed25b5dd709aa30dc679ccc5817f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 May 2023 12:03:15 +0200 Subject: [PATCH 439/806] Fix cstring literals construct &CStr not &str --- crates/hir-def/src/lang_item.rs | 270 ++++++++++++++------------- crates/hir-ty/src/consteval/tests.rs | 32 ++++ crates/hir-ty/src/infer/expr.rs | 18 +- crates/hir-ty/src/tests/simple.rs | 15 ++ 4 files changed, 205 insertions(+), 130 deletions(-) diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 253e2daeddf68..0e9ac58fbaaad 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -199,7 +199,7 @@ pub enum GenericRequirement { macro_rules! language_item_table { ( - $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )* + $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* ) => { /// A representation of all the valid language items in Rust. @@ -244,82 +244,86 @@ impl LangItem { language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; - Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); - Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); + Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); + Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). - StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; + StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; /// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize). - StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None; - Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); - Clone, clone, clone_trait, Target::Trait, GenericRequirement::None; - Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); - DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; + StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None; + Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); + Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; + Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); + DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the [`DiscriminantKind`] trait. - Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None; + Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None; - PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; - Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; - DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; + PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; + Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; + DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; - Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); + Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); - Drop, drop, drop_trait, Target::Trait, GenericRequirement::None; - Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None; + FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0); + FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); - DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); + Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; + Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; + + CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); + DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); // language items relating to transmutability - TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); - TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); - - Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1); - Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); - Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); - Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1); - Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); - Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); - Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0); - BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); - BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); - BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); - Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); - Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); - AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); - SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); - MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); - DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); - RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); - Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1); - IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); - - UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; - VaList, va_list, va_list, Target::Struct, GenericRequirement::None; - - Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); - DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); - DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None; - Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None; - - Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - - FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; - - Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); - GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None; - Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); - Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None; - Pin, pin, pin_type, Target::Struct, GenericRequirement::None; - - PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); + TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); + + Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1); + Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); + Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); + Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1); + Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); + Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); + Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0); + BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); + BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); + BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); + Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); + Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); + AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); + SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); + MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); + DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); + RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); + Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1); + IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); + + UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; + + Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); + DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); + DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; + Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + + Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; + + Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); + GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None; + Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); + Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; + Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + + PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. @@ -328,93 +332,103 @@ language_item_table! { // in the sense that a crate is not required to have it defined to use it, but a final product // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. - Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); - PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); - PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; - PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None; - ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; - PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); - PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None; - PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None; - PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None; - PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); + Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); + PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); + PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; + PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; + ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; + PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); + PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0); + PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; + PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; + PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; + PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); /// libstd panic entry point. Necessary for const eval to be able to catch it - BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; + BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; + + // Lang items needed for `format_args!()`. + FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None; + FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None; + FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None; + FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None; + FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None; + FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None; - ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; - BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); - DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); - AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; + ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; + BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); + DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); + AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; - Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1); + Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); - EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None; - EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None; + EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None; + EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None; - OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); + OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); - PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); + PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); - ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; - MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; + MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; /// Align offset for stride != 1; must not panic. - AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None; + AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None; - Termination, termination, termination, Target::Trait, GenericRequirement::None; + Termination, sym::termination, termination, Target::Trait, GenericRequirement::None; - Try, Try, try_trait, Target::Trait, GenericRequirement::None; + Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None; - Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0); + Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0); - SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; // Language items from AST lowering - TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; - PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0); + PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); - Poll, Poll, poll, Target::Enum, GenericRequirement::None; - PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; - PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; + ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); + + Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; + PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; + PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; // FIXME(swatinem): the following lang items are used for async lowering and // should become obsolete eventually. - ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; - IdentityFuture, identity_future, identity_future_fn, Target::Fn, GenericRequirement::None; - GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None; - - Context, Context, context, Target::Struct, GenericRequirement::None; - FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; + GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None; - FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + Context, sym::Context, context, Target::Struct, GenericRequirement::None; + FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None; - OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None; + Option, sym::Option, option_type, Target::Enum, GenericRequirement::None; + OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None; + OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None; - ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None; - ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None; + ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None; + ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None; - ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None; - ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None; + ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None; + ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None; - IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; + IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; - PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; - RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None; - RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None; - RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None; - RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None; - Range, Range, range_struct, Target::Struct, GenericRequirement::None; - RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; - RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None; + RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None; + RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None; + RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None; + Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; + RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; + RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; - String, String, string, Target::Struct, GenericRequirement::None; + String, sym::String, string, Target::Struct, GenericRequirement::None; + CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 41e0b5188b38c..167854f3eddc4 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1829,6 +1829,38 @@ fn byte_string() { ); } +#[test] +fn c_string() { + check_number( + r#" +//- minicore: index, slice +#[lang = "CStr"] +pub struct CStr { + inner: [u8] +} +const GOAL: u8 = { + let a = c"hello"; + a.inner[0] +}; + "#, + 104, + ); + check_number( + r#" +//- minicore: index, slice +#[lang = "CStr"] +pub struct CStr { + inner: [u8] +} +const GOAL: u8 = { + let a = c"hello"; + a.inner[6] +}; + "#, + 0, + ); +} + #[test] fn consts() { check_number( diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 385766fdad0bf..56438866de0db 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -13,7 +13,7 @@ use hir_def::{ hir::{ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, }, - lang_item::LangItem, + lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs}, BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, }; @@ -815,7 +815,7 @@ impl<'a> InferenceContext<'a> { Expr::Array(array) => self.infer_expr_array(array, expected), Expr::Literal(lit) => match lit { Literal::Bool(..) => self.result.standard_types.bool_.clone(), - Literal::String(..) | Literal::CString(..) => { + Literal::String(..) => { TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner)) .intern(Interner) } @@ -831,6 +831,20 @@ impl<'a> InferenceContext<'a> { let array_type = TyKind::Array(byte_type, len).intern(Interner); TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner) } + Literal::CString(..) => TyKind::Ref( + Mutability::Not, + static_lifetime(), + self.resolve_lang_item(LangItem::CStr) + .and_then(LangItemTarget::as_struct) + .map_or_else( + || self.err_ty(), + |strukt| { + TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner)) + .intern(Interner) + }, + ), + ) + .intern(Interner), Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner), Literal::Int(_v, ty) => match ty { Some(int_ty) => { diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index e249cddc2fc35..942d693140f7e 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3557,3 +3557,18 @@ fn main() { "#, ); } + +#[test] +fn cstring_literals() { + check_types( + r#" +#[lang = "CStr"] +pub struct CStr; + +fn main() { + c"ello"; + //^^^^^^^ &CStr +} +"#, + ); +} From 440912b74f5ac063d8993741120a519bed3882f2 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 17 May 2023 17:29:02 -0400 Subject: [PATCH 440/806] Option::map_or_else: Show an example of integrating with Result Moving this from https://github.com/rust-lang/libs-team/issues/59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least. --- library/core/src/option.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ec1ef3cf43d12..b93b40e300361 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1138,7 +1138,7 @@ impl Option { /// Computes a default function result (if none), or /// applies a different function to the contained value (if any). /// - /// # Examples + /// # Basic examples /// /// ``` /// let k = 21; @@ -1149,6 +1149,25 @@ impl Option { /// let x: Option<&str> = None; /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); /// ``` + /// + /// # Handling a Result-based fallback + /// + /// A somewhat common occurrence when dealing with optional values + /// in combination with [`Result`] is the case where one wants to invoke + /// a fallible fallback if the option is not present. This example + /// parses a command line argument (if present), or the contents of a file to + /// an integer. However, unlike accessing the command line argument, reading + /// the file is fallible, so it must be wrapped with `Ok`. + /// + /// ```no_run + /// # fn main() -> Result<(), Box> { + /// let v: u64 = std::env::args() + /// .nth(1) + /// .map_or_else(|| std::fs::read_to_string("/etc/someconfig.conf"), Ok)? + /// .parse()?; + /// # Ok(()) + /// # } + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn map_or_else(self, default: D, f: F) -> U From 4adfbbfbadf0fb74dee05d4bc5110e00596c5919 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 18:30:49 +0330 Subject: [PATCH 441/806] partially support panic message in `MirEvalError` --- crates/hir-def/src/body.rs | 9 +++ crates/hir-def/src/body/pretty.rs | 11 +++ .../macro_expansion_tests/builtin_fn_macro.rs | 10 +-- crates/hir-expand/src/builtin_fn_macro.rs | 12 ++- crates/hir-expand/src/name.rs | 2 + crates/hir-ty/src/consteval/tests.rs | 39 +++++++--- crates/hir-ty/src/mir/eval/shim.rs | 47 +++++++++++- crates/hir-ty/src/mir/lower.rs | 22 +++++- crates/hir-ty/src/tests/regression.rs | 7 +- crates/ide/src/inlay_hints/chaining.rs | 12 +-- crates/test-utils/src/minicore.rs | 73 +++++++++++++++++-- 11 files changed, 199 insertions(+), 45 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index d4b4cbe6c7cef..ea682f5cbde35 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -187,6 +187,15 @@ impl Body { pretty::print_body_hir(db, self, owner) } + pub fn pretty_print_expr( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr: ExprId, + ) -> String { + pretty::print_expr_hir(db, self, owner, expr) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 093dd67970248..318f654c16fa1 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -61,6 +61,17 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo p.buf } +pub(super) fn print_expr_hir( + _db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + expr: ExprId, +) -> String { + let mut p = Printer { body, buf: String::new(), indent_level: 0, needs_indent: false }; + p.print_expr(expr); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index f5346898c2029..13aa6130be8b6 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -201,7 +201,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -229,7 +229,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -262,7 +262,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[$crate::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), $crate::fmt::Display::fmt), ]); + ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); } "##]], ); @@ -296,7 +296,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[$crate::fmt::ArgumentV1::new(&(2), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Debug::fmt), ]); + ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); } "##]], ); @@ -327,7 +327,7 @@ macro_rules! format_args { fn main() { let _ = /* error: no rule matches input tokens *//* parse error: expected field name or number */ -$crate::fmt::Arguments::new_v1(&["", " ", ], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(), $crate::fmt::Debug::fmt), ]); +::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]); } "##]], ); diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index e9e1c6c3b3340..0640ba774bb04 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -363,16 +363,14 @@ fn format_args_expand_general( quote!(#ident) }; let formatter = match &*format_spec { - "?" => quote!(#DOLLAR_CRATE::fmt::Debug::fmt), - "" => quote!(#DOLLAR_CRATE::fmt::Display::fmt), + "?" => quote!(::core::fmt::Debug::fmt), + "" => quote!(::core::fmt::Display::fmt), _ => { // FIXME: implement the rest and return expand error here - quote!(#DOLLAR_CRATE::fmt::Display::fmt) + quote!(::core::fmt::Display::fmt) } }; - arg_tts.push( - quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }, - ); + arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }); } '}' => { if format_iter.peek() == Some(&'}') { @@ -400,7 +398,7 @@ fn format_args_expand_general( }); let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); let expanded = quote! { - #DOLLAR_CRATE::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) + ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) }; ExpandResult { value: expanded, err } } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index d6bad1a48c17a..10a251ba78433 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -362,6 +362,8 @@ pub mod known { gt, le, lt, + // known fields of lang items + pieces, // lang items add_assign, add, diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 5ab1f970201f4..b4b5fdd8916c4 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -26,9 +26,11 @@ fn simplify(e: ConstEvalError) -> ConstEvalError { #[track_caller] fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) { let (db, file_id) = TestDB::with_single_file(ra_fixture); - match eval_goal(&db, file_id).map_err(simplify) { + match eval_goal(&db, file_id) { Ok(_) => panic!("Expected fail, but it succeeded"), - Err(e) => assert!(error(e)), + Err(e) => { + assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db)) + } } } @@ -38,13 +40,7 @@ fn check_number(ra_fixture: &str, answer: i128) { let r = match eval_goal(&db, file_id) { Ok(t) => t, Err(e) => { - let mut err = String::new(); - let span_formatter = |file, range| format!("{:?} {:?}", file, range); - match e { - ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter), - ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter), - } - .unwrap(); + let err = pretty_print_err(e, db); panic!("Error in evaluating goal: {}", err); } }; @@ -64,6 +60,17 @@ fn check_number(ra_fixture: &str, answer: i128) { } } +fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { + let mut err = String::new(); + let span_formatter = |file, range| format!("{:?} {:?}", file, range); + match e { + ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter), + ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter), + } + .unwrap(); + err +} + fn eval_goal(db: &TestDB, file_id: FileId) -> Result { let module_id = db.module_for_file(file_id); let def_map = module_id.def_map(db); @@ -2187,6 +2194,20 @@ fn const_trait_assoc() { ); } +#[test] +fn panic_messages() { + check_fail( + r#" + //- minicore: panic + const GOAL: u8 = { + let x: u16 = 2; + panic!("hello"); + }; + "#, + |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())), + ); +} + #[test] fn exec_limits() { check_fail( diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index ac295b58375bd..9ff58b27bb47f 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -153,10 +153,49 @@ impl Evaluator<'_> { use LangItem::*; let mut args = args.iter(); match x { - // FIXME: we want to find the panic message from arguments, but it wouldn't work - // currently even if we do that, since macro expansion of panic related macros - // is dummy. - PanicFmt | BeginPanic => Err(MirEvalError::Panic("".to_string())), + BeginPanic => Err(MirEvalError::Panic("".to_string())), + PanicFmt => { + let message = (|| { + let arguments_struct = + self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?; + let arguments_layout = self + .layout_adt(arguments_struct.into(), Substitution::empty(Interner)) + .ok()?; + let arguments_field_pieces = + self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?; + let pieces_offset = arguments_layout + .fields + .offset(u32::from(arguments_field_pieces.into_raw()) as usize) + .bytes_usize(); + let ptr_size = self.ptr_size(); + let arg = args.next()?; + let pieces_array_addr = + Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?; + let pieces_array_len = usize::from_le_bytes( + (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size]) + .try_into() + .ok()?, + ); + let mut message = "".to_string(); + for i in 0..pieces_array_len { + let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size); + let piece_addr = + Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?) + .ok()?; + let piece_len = usize::from_le_bytes( + self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size) + .ok()? + .try_into() + .ok()?, + ); + let piece_data = self.read_memory(piece_addr, piece_len).ok()?; + message += &std::string::String::from_utf8_lossy(piece_data); + } + Some(message) + })() + .unwrap_or_else(|| "".to_string()); + Err(MirEvalError::Panic(message)) + } SliceLen => { let arg = args .next() diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4bf8070fa46a9..292a771baf919 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -81,7 +81,7 @@ pub enum MirLowerError { UnresolvedMethod(String), UnresolvedField, UnsizedTemporary(Ty), - MissingFunctionDefinition, + MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch), /// This should be never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -113,6 +113,22 @@ impl MirLowerError { ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, } } + MirLowerError::MissingFunctionDefinition(owner, x) => { + let body = db.body(*owner); + writeln!( + f, + "Missing function definition for {}", + body.pretty_print_expr(db.upcast(), *owner, *x) + )?; + } + MirLowerError::TypeMismatch(e) => { + writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db), + e.actual.display(db), + )?; + } MirLowerError::LayoutError(_) | MirLowerError::UnsizedTemporary(_) | MirLowerError::IncompleteExpr @@ -122,8 +138,6 @@ impl MirLowerError { | MirLowerError::RecordLiteralWithoutPath | MirLowerError::UnresolvedMethod(_) | MirLowerError::UnresolvedField - | MirLowerError::MissingFunctionDefinition - | MirLowerError::TypeMismatch(_) | MirLowerError::TypeError(_) | MirLowerError::NotSupported(_) | MirLowerError::ContinueWithoutLoop @@ -599,7 +613,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) } - TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), + TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), _ => return Err(MirLowerError::TypeError("function call on bad type")), } } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 4af143829f3da..9f5f1ea3255aa 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1475,13 +1475,12 @@ fn regression_11688_3() { struct Ar(T); fn f( num_zeros: usize, - ) -> dyn Iterator; LEN]> { + ) -> &dyn Iterator; LEN]> { loop {} } fn dynamic_programming() { - for board in f::<9, u8, 7>(1) { - //^^^^^ [Ar; 9] - } + let board = f::<9, u8, 7>(1).next(); + //^^^^^ Option<[Ar; 9]> } "#, ); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 451c15a25c3bb..db98bf2f9bcbe 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 5769..5777, + range: 9164..9172, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 5801..5805, + range: 9196..9200, }, ), tooltip: "", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 960934cb69d05..22cef04983894 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -23,7 +23,7 @@ //! drop: //! eq: sized //! error: fmt -//! fmt: result +//! fmt: result, transmute, coerce_unsized //! fn: //! from: sized //! future: pin @@ -37,7 +37,7 @@ //! non_zero: //! option: panic //! ord: eq, option -//! panic: +//! panic: fmt //! pin: //! range: //! result: @@ -45,6 +45,7 @@ //! sized: //! slice: //! sync: sized +//! transmute: //! try: infallible //! unsize: sized @@ -289,8 +290,8 @@ pub mod convert { // endregion:infallible } -// region:drop pub mod mem { + // region:drop // region:manually_drop #[lang = "manually_drop"] #[repr(transparent)] @@ -323,15 +324,23 @@ pub mod mem { result } } + // endregion:drop + + // region:transmute + extern "rust-intrinsic" { + pub fn transmute(src: Src) -> Dst; + } + // endregion:transmute } pub mod ptr { + // region:drop #[lang = "drop_in_place"] pub unsafe fn drop_in_place(to_drop: *mut T) { unsafe { drop_in_place(to_drop) } } + // endregion:drop } -// endregion:drop pub mod ops { // region:coerce_unsized @@ -812,6 +821,38 @@ pub mod fmt { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + extern "C" { + type Opaque; + } + + #[lang = "format_argument"] + pub struct ArgumentV1<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, + } + + impl<'a> ArgumentV1<'a> { + pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + use crate::mem::transmute; + unsafe { ArgumentV1 { formatter: transmute(f), value: transmute(x) } } + } + } + + #[lang = "format_arguments"] + pub struct Arguments<'a> { + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + } + + impl<'a> Arguments<'a> { + pub const fn new_v1( + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + ) -> Arguments<'a> { + Arguments { pieces, args } + } + } + // region:derive #[rustc_builtin_macro] pub macro Debug($item:item) {} @@ -1147,8 +1188,17 @@ pub mod iter { // region:panic mod panic { - pub macro panic_2021($($t:tt)+) { - /* Nothing yet */ + pub macro panic_2021 { + ($($t:tt)+) => ( + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) + ), + } +} + +mod panicking { + #[lang = "panic_fmt"] + pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + loop {} } } // endregion:panic @@ -1166,6 +1216,17 @@ mod macros { pub(crate) use panic; // endregion:panic + // region:fmt + #[macro_export] + #[rustc_builtin_macro] + macro_rules! const_format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + pub(crate) use const_format_args; + // endregion:fmt + // region:derive pub(crate) mod builtin { #[rustc_builtin_macro] From b55fbd3ad7eea6492815c0cc1e3c6adf0ebf246c Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 19:17:06 +0330 Subject: [PATCH 442/806] Add `moved-out-of-ref` diagnostic --- crates/hir-ty/src/infer/expr.rs | 13 +- crates/hir-ty/src/mir/borrowck.rs | 118 +++++++++++++- crates/hir/src/diagnostics.rs | 7 + crates/hir/src/lib.rs | 29 +++- .../src/handlers/missing_match_arms.rs | 2 + .../src/handlers/missing_unsafe.rs | 5 + .../src/handlers/moved_out_of_ref.rs | 154 ++++++++++++++++++ .../src/handlers/mutability_errors.rs | 1 + crates/ide-diagnostics/src/lib.rs | 2 + crates/ide-diagnostics/src/tests.rs | 14 ++ crates/test-utils/src/minicore.rs | 17 +- 11 files changed, 352 insertions(+), 10 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3261c22313bf0..b800c2e32a9d5 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -934,7 +934,18 @@ impl<'a> InferenceContext<'a> { match fn_x { FnTrait::FnOnce => (), FnTrait::FnMut => { - if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Mut, _, _)) { + if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) { + if adjustments + .last() + .map(|x| matches!(x.kind, Adjust::Borrow(_))) + .unwrap_or(true) + { + // prefer reborrow to move + adjustments + .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() }); + adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone())) + } + } else { adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone())); } } diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index a6af4e75d45d7..412390d3fa1d4 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -5,12 +5,14 @@ use std::iter; -use hir_def::DefWithBodyId; +use hir_def::{DefWithBodyId, HasModule}; use la_arena::ArenaMap; use stdx::never; use triomphe::Arc; -use crate::{db::HirDatabase, ClosureId}; +use crate::{ + db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt, TypeFlags, +}; use super::{ BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem, @@ -24,10 +26,17 @@ pub enum MutabilityReason { Not, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MovedOutOfRef { + pub ty: Ty, + pub span: MirSpan, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct BorrowckResult { pub mir_body: Arc, pub mutability_of_locals: ArenaMap, + pub moved_out_of_ref: Vec, } fn all_mir_bodies( @@ -68,12 +77,115 @@ pub fn borrowck_query( let r = all_mir_bodies(db, def) .map(|body| { let body = body?; - Ok(BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body }) + Ok(BorrowckResult { + mutability_of_locals: mutability_of_locals(&body), + moved_out_of_ref: moved_out_of_ref(db, &body), + mir_body: body, + }) }) .collect::, MirLowerError>>()?; Ok(r.into()) } +fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec { + let mut result = vec![]; + let mut for_operand = |op: &Operand, span: MirSpan| match op { + Operand::Copy(p) | Operand::Move(p) => { + let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut is_dereference_of_ref = false; + for proj in &p.projection { + if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { + is_dereference_of_ref = true; + } + ty = proj.projected_ty( + ty, + db, + |c, subst, f| { + let (def, _) = db.lookup_intern_closure(c.into()); + let infer = db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + body.owner.module(db.upcast()).krate(), + ); + } + if is_dereference_of_ref + && !ty.clone().is_copy(db, body.owner) + && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) + { + result.push(MovedOutOfRef { span, ty }); + } + } + Operand::Constant(_) | Operand::Static(_) => (), + }; + for (_, block) in body.basic_blocks.iter() { + for statement in &block.statements { + match &statement.kind { + StatementKind::Assign(_, r) => match r { + Rvalue::ShallowInitBoxWithAlloc(_) => (), + Rvalue::ShallowInitBox(o, _) + | Rvalue::UnaryOp(_, o) + | Rvalue::Cast(_, o, _) + | Rvalue::Repeat(o, _) + | Rvalue::Use(o) => for_operand(o, statement.span), + Rvalue::CopyForDeref(_) + | Rvalue::Discriminant(_) + | Rvalue::Len(_) + | Rvalue::Ref(_, _) => (), + Rvalue::CheckedBinaryOp(_, o1, o2) => { + for_operand(o1, statement.span); + for_operand(o2, statement.span); + } + Rvalue::Aggregate(_, ops) => { + for op in ops { + for_operand(op, statement.span); + } + } + }, + StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + match &block.terminator { + Some(terminator) => match &terminator.kind { + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span), + TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::GeneratorDrop + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } => (), + TerminatorKind::DropAndReplace { value, .. } => { + for_operand(value, terminator.span); + } + TerminatorKind::Call { func, args, .. } => { + for_operand(func, terminator.span); + args.iter().for_each(|x| for_operand(x, terminator.span)); + } + TerminatorKind::Assert { cond, .. } => { + for_operand(cond, terminator.span); + } + TerminatorKind::Yield { value, .. } => { + for_operand(value, terminator.span); + } + }, + None => (), + } + } + result +} + fn is_place_direct(lvalue: &Place) -> bool { !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref) } diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index f81f8b0b011e9..10893b62bfb57 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -46,6 +46,7 @@ diagnostics![ MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, @@ -252,3 +253,9 @@ pub struct NeedMut { pub struct UnusedMut { pub local: Local, } + +#[derive(Debug)] +pub struct MovedOutOfRef { + pub ty: Type, + pub span: InFile, +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 42047446144df..bc925552f34a5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -90,10 +90,11 @@ pub use crate::{ AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, - NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, - TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, - UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, - UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, + ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, + UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, + UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, + UnusedMut, }, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, @@ -1575,6 +1576,26 @@ impl DefWithBody { if let Ok(borrowck_results) = db.borrowck(self.into()) { for borrowck_result in borrowck_results.iter() { let mir_body = &borrowck_result.mir_body; + for moof in &borrowck_result.moved_out_of_ref { + let span: InFile = match moof.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( + MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span } + .into(), + ) + } let mol = &borrowck_result.mutability_of_locals; for (binding_id, _) in hir_body.bindings.iter() { let Some(&local) = mir_body.binding_locals.get(binding_id) else { diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index b5e619e2a039c..3f13b97a44734 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -1027,6 +1027,7 @@ fn main() { check_diagnostics( r#" +//- minicore: copy fn main() { match &false { &true => {} @@ -1041,6 +1042,7 @@ fn main() { cov_mark::check_count!(validate_match_bailed_out, 1); check_diagnostics( r#" +//- minicore: copy fn main() { match (&false,) { //^^^^^^^^^ error: missing match arm: `(&false,)` not covered diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index d73f4e7721c29..2026b6fcef537 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -142,6 +142,8 @@ fn main() { fn missing_unsafe_diagnostic_with_static_mut() { check_diagnostics( r#" +//- minicore: copy + struct Ty { a: u8, } @@ -256,6 +258,7 @@ fn main() { fn add_unsafe_block_when_accessing_mutable_static() { check_fix( r#" +//- minicore: copy struct Ty { a: u8, } @@ -374,6 +377,7 @@ fn main() { fn unsafe_expr_as_right_hand_side_of_assignment() { check_fix( r#" +//- minicore: copy static mut STATIC_MUT: u8 = 0; fn main() { @@ -396,6 +400,7 @@ fn main() { fn unsafe_expr_in_binary_plus() { check_fix( r#" +//- minicore: copy static mut STATIC_MUT: u8 = 0; fn main() { diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs new file mode 100644 index 0000000000000..99243a5ab8677 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -0,0 +1,154 @@ +use crate::{Diagnostic, DiagnosticsContext}; +use hir::HirDisplay; + +// Diagnostic: moved-out-of-ref +// +// This diagnostic is triggered on moving non copy things out of references. +pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic { + Diagnostic::new( + "moved-out-of-ref", + format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.span.clone()).range, + ) + .experimental() // spans are broken, and I'm not sure how precise we can detect copy types +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + // FIXME: spans are broken + + #[test] + fn move_by_explicit_deref() { + check_diagnostics( + r#" +struct X; +fn main() { + let a = &X; + let b = *a; + //^ error: cannot move `X` out of reference +} +"#, + ); + } + + #[test] + fn move_out_of_field() { + check_diagnostics( + r#" +//- minicore: copy +struct X; +struct Y(X, i32); +fn main() { + let a = &Y(X, 5); + let b = a.0; + //^ error: cannot move `X` out of reference + let y = a.1; +} +"#, + ); + } + + #[test] + fn move_out_of_static() { + check_diagnostics( + r#" +//- minicore: copy +struct X; +fn main() { + static S: X = X; + let s = S; + //^ error: cannot move `X` out of reference +} +"#, + ); + } + + #[test] + fn generic_types() { + check_diagnostics( + r#" +//- minicore: derive, copy + +#[derive(Copy)] +struct X(T); +struct Y; + +fn consume(_: X) { + +} + +fn main() { + let a = &X(Y); + consume(*a); + //^^^^^^^^^^^ error: cannot move `X` out of reference + let a = &X(5); + consume(*a); +} +"#, + ); + } + + #[test] + fn no_false_positive_simple() { + check_diagnostics( + r#" +//- minicore: copy +fn f(_: i32) {} +fn main() { + let x = &2; + f(*x); +} +"#, + ); + } + + #[test] + fn no_false_positive_unknown_type() { + check_diagnostics( + r#" +//- minicore: derive, copy +fn f(x: &Unknown) -> Unknown { + *x +} + +#[derive(Copy)] +struct X(T); + +struct Y(T); + +fn g(x: &X) -> X { + *x +} + +fn h(x: &Y) -> Y { + // FIXME: we should show error for this, as `Y` is not copy + // regardless of its generic parameter. + *x +} + +"#, + ); + } + + #[test] + fn no_false_positive_dyn_fn() { + check_diagnostics( + r#" +//- minicore: copy, fn +fn f(x: &mut &mut dyn Fn()) { + x(); +} + +struct X<'a> { + field: &'a mut dyn Fn(), +} + +fn f(x: &mut X<'_>) { + (x.field)(); +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index d60007b48a6d8..9d6a862cb1192 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -340,6 +340,7 @@ fn main() { fn regression_14310() { check_diagnostics( r#" + //- minicore: copy, builtin_impls fn clone(mut i: &!) -> ! { //^^^^^ 💡 weak: variable does not need to be mutable *i diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 25d3568950dfe..048dedf6bd15e 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -38,6 +38,7 @@ mod handlers { pub(crate) mod missing_fields; pub(crate) mod missing_match_arms; pub(crate) mod missing_unsafe; + pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; pub(crate) mod no_such_field; pub(crate) mod private_assoc_item; @@ -283,6 +284,7 @@ pub fn diagnostics( AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), + AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 413689561bc1c..b5cd4e0d68911 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -121,6 +121,15 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur }) .collect::>(); actual.sort_by_key(|(range, _)| range.start()); + if expected.is_empty() { + // makes minicore smoke test debugable + for (e, _) in &actual { + eprintln!( + "Code in range {e:?} = {}", + &db.file_text(file_id)[usize::from(e.start())..usize::from(e.end())] + ) + } + } assert_eq!(expected, actual); } } @@ -156,6 +165,11 @@ fn minicore_smoke_test() { // Checks that there is no diagnostic in minicore for each flag. for flag in MiniCore::available_flags() { + if flag == "clone" { + // Clone without copy has `moved-out-of-ref`, so ignoring. + // FIXME: Maybe we should merge copy and clone in a single flag? + continue; + } eprintln!("Checking minicore flag {flag}"); check(MiniCore::from_flags([flag])); } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 22cef04983894..6d6c9af7f0462 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -111,6 +111,7 @@ pub mod marker { impl Copy for *const T {} impl Copy for *mut T {} impl Copy for &T {} + impl Copy for ! {} } // endregion:copy @@ -246,6 +247,12 @@ pub mod clone { f32 f64 bool char } + + impl Clone for ! { + fn clone(&self) { + *self + } + } // endregion:builtin_impls // region:derive @@ -319,8 +326,8 @@ pub mod mem { pub fn drop(_x: T) {} pub const fn replace(dest: &mut T, src: T) -> T { unsafe { - let result = *dest; - *dest = src; + let result = crate::ptr::read(dest); + crate::ptr::write(dest, src); result } } @@ -339,6 +346,12 @@ pub mod ptr { pub unsafe fn drop_in_place(to_drop: *mut T) { unsafe { drop_in_place(to_drop) } } + pub const unsafe fn read(src: *const T) -> T { + *src + } + pub const unsafe fn write(dst: *mut T, src: T) { + *dst = src; + } // endregion:drop } From cae9660a1d076ddd78f28e65a7ac78b2c663ddda Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 18 May 2023 21:03:03 +0330 Subject: [PATCH 443/806] Add layout info for enum variant and locals --- crates/hir/src/lib.rs | 50 +++++++--- crates/ide/src/hover/render.rs | 58 +++++++++-- crates/ide/src/hover/tests.rs | 171 ++++++++++++++++++--------------- 3 files changed, 184 insertions(+), 95 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bc925552f34a5..8fac7fcd874ab 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, lang_item::LangItemTarget, - layout::ReprOptions, + layout::{self, ReprOptions}, macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, @@ -62,7 +62,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{layout_of_ty, Layout, LayoutError}, + layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -1089,7 +1089,7 @@ impl Enum { Type::new_for_crate( self.id.lookup(db.upcast()).container.krate(), TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() { - hir_def::layout::IntegerType::Pointer(sign) => match sign { + layout::IntegerType::Pointer(sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int( hir_def::builtin_type::BuiltinInt::Isize, ), @@ -1097,20 +1097,20 @@ impl Enum { hir_def::builtin_type::BuiltinUint::Usize, ), }, - hir_def::layout::IntegerType::Fixed(i, sign) => match sign { + layout::IntegerType::Fixed(i, sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, }), false => hir_def::builtin_type::BuiltinType::Uint(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, }), }, }), @@ -1177,6 +1177,28 @@ impl Variant { pub fn eval(self, db: &dyn HirDatabase) -> Result { db.const_eval_discriminant(self.into()) } + + /// Return layout of the variant and tag size of the parent enum. + pub fn layout(&self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { + let parent_enum = self.parent_enum(db); + let parent_layout = Adt::from(parent_enum).layout(db)?; + if let layout::Variants::Multiple { variants, tag, tag_encoding, tag_field: _ } = + parent_layout.variants + { + let tag_size = match tag_encoding { + TagEncoding::Direct => { + let target_data_layout = db + .target_data_layout(parent_enum.module(db).krate().id) + .ok_or(LayoutError::TargetLayoutNotAvailable)?; + tag.size(&*target_data_layout).bytes_usize() + } + TagEncoding::Niche { .. } => 0, + }; + Ok((variants[RustcEnumVariantIdx(self.id)].clone(), tag_size)) + } else { + Ok((parent_layout, 0)) + } + } } /// Variants inherit visibility from the parent enum. diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index c2b9222cb9598..fffc837876af8 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -417,15 +417,25 @@ pub(super) fn definition( let layout = it.layout(db).ok()?; Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) }), - Definition::Variant(it) => label_value_and_docs(db, it, |&it| { - if !it.parent_enum(db).is_data_carrying(db) { + Definition::Variant(it) => label_value_and_layout_info_and_docs(db, it, config, |&it| { + let layout = (|| { + let (layout, tag_size) = it.layout(db).ok()?; + let size = layout.size.bytes_usize() - tag_size; + if size == 0 { + // There is no value in showing layout info for fieldless variants + return None; + } + Some(format!("size = {}", layout.size.bytes())) + })(); + let value = if !it.parent_enum(db).is_data_carrying(db) { match it.eval(db) { Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }), Err(_) => it.value(db).map(|x| format!("{x:?}")), } } else { None - } + }; + (value, layout) }), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); @@ -460,7 +470,7 @@ pub(super) fn definition( .and_then(|fd| builtin(fd, it)) .or_else(|| Some(Markup::fenced_block(&it.name()))) } - Definition::Local(it) => return local(db, it), + Definition::Local(it) => return local(db, it, config), Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } @@ -637,6 +647,32 @@ where (label, docs) } +fn label_value_and_layout_info_and_docs( + db: &RootDatabase, + def: D, + config: &HoverConfig, + value_extractor: E, +) -> (String, Option) +where + D: HasAttrs + HirDisplay, + E: Fn(&D) -> (Option, Option), + V: Display, + L: Display, +{ + let (value, layout) = value_extractor(&def); + let label = if let Some(value) = value { + format!("{} = {value}", def.display(db)) + } else { + def.display(db).to_string() + }; + let label = match layout { + Some(layout) if config.memory_layout => format!("{} // {layout}", label), + _ => label, + }; + let docs = def.attrs(db).docs(); + (label, docs) +} + fn label_value_and_docs( db: &RootDatabase, def: D, @@ -696,11 +732,11 @@ fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option Option { +fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> 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.primary_source(db).into_ident_pat() { + let mut desc = match it.primary_source(db).into_ident_pat() { Some(ident) => { let name = it.name(db); let let_kw = if ident @@ -716,6 +752,16 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option { } None => format!("{is_mut}self: {ty}"), }; + if config.memory_layout { + if let Ok(layout) = it.ty(db).layout(db) { + format_to!( + desc, + " // size = {}, align = {}", + layout.size.bytes(), + layout.align.abi.bytes() + ); + } + } markup(None, desc, None) } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 0d8fc8a5f72f2..60708cb42ed86 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -150,7 +150,7 @@ fn foo() { *local* ```rust - let local: i32 + let local: i32 // size = 4, align = 4 ``` "#]], ); @@ -396,12 +396,12 @@ fn main() { } "#, expect![[r#" - *iter* + *iter* - ```rust - let mut iter: Iter>, impl Fn(&mut u32, &u32, &mut u32) -> Option, u32>> - ``` - "#]], + ```rust + let mut iter: Iter>, impl Fn(&mut u32, &u32, &mut u32) -> Option, u32>> // size = 8, align = 4 + ``` + "#]], ); } @@ -778,12 +778,12 @@ fn main() { let zz$0 = Test { t: 23u8, k: 33 }; }"#, expect![[r#" - *zz* + *zz* - ```rust - let zz: Test - ``` - "#]], + ```rust + let zz: Test // size = 8, align = 4 + ``` + "#]], ); check_hover_range( r#" @@ -829,12 +829,12 @@ use Option::Some; fn main() { let b$0ar = Some(12); } "#, expect![[r#" - *bar* + *bar* - ```rust - let bar: Option - ``` - "#]], + ```rust + let bar: Option // size = 4, align = 4 + ``` + "#]], ); } @@ -898,12 +898,12 @@ fn hover_for_local_variable() { check( r#"fn func(foo: i32) { fo$0o; }"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -912,12 +912,12 @@ fn hover_for_local_variable_pat() { check( r#"fn func(fo$0o: i32) {}"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -926,12 +926,12 @@ fn hover_local_var_edge() { check( r#"fn func(foo: i32) { if true { $0foo; }; }"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -940,12 +940,12 @@ fn hover_for_param_edge() { check( r#"fn func($0foo: i32) {}"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -984,12 +984,12 @@ impl Thing { fn main() { let foo_$0test = Thing::new(); } "#, expect![[r#" - *foo_test* + *foo_test* - ```rust - let foo_test: Thing - ``` - "#]], + ```rust + let foo_test: Thing // size = 4, align = 4 + ``` + "#]], ) } @@ -1144,12 +1144,12 @@ fn y() { } "#, expect![[r#" - *x* + *x* - ```rust - let x: i32 - ``` - "#]], + ```rust + let x: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -1274,12 +1274,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } } fn foo(bar:u32) { let a = id!(ba$0r); } "#, expect![[r#" - *bar* + *bar* - ```rust - bar: u32 - ``` - "#]], + ```rust + bar: u32 // size = 4, align = 4 + ``` + "#]], ); } @@ -1292,12 +1292,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } fn foo(bar:u32) { let a = id!(ba$0r); } "#, expect![[r#" - *bar* + *bar* - ```rust - bar: u32 - ``` - "#]], + ```rust + bar: u32 // size = 4, align = 4 + ``` + "#]], ); } @@ -1841,6 +1841,27 @@ pub fn fo$0o() {} ); } +#[test] +fn test_hover_layout_of_variant() { + check( + r#"enum Foo { + Va$0riant1(u8, u16), + Variant2(i32, u8, i64), + }"#, + expect![[r#" + *Variant1* + + ```rust + test::Foo + ``` + + ```rust + Variant1(u8, u16) // size = 4 + ``` + "#]], + ); +} + #[test] fn test_hover_no_memory_layout() { check_hover_no_memory_layout( @@ -3135,7 +3156,7 @@ fn main() { *f* ```rust - f: &i32 + f: &i32 // size = 8, align = 8 ``` --- @@ -3185,7 +3206,7 @@ fn main() { *value* ```rust - let value: Const<1> + let value: Const<1> // size = 0, align = 1 ``` "#]], ); @@ -3205,7 +3226,7 @@ fn main() { *value* ```rust - let value: Const<0> + let value: Const<0> // size = 0, align = 1 ``` "#]], ); @@ -3225,7 +3246,7 @@ fn main() { *value* ```rust - let value: Const<-1> + let value: Const<-1> // size = 0, align = 1 ``` "#]], ); @@ -3245,7 +3266,7 @@ fn main() { *value* ```rust - let value: Const + let value: Const // size = 0, align = 1 ``` "#]], ); @@ -3265,7 +3286,7 @@ fn main() { *value* ```rust - let value: Const<'🦀'> + let value: Const<'🦀'> // size = 0, align = 1 ``` "#]], ); @@ -3281,12 +3302,12 @@ impl Foo { } "#, expect![[r#" - *self* + *self* - ```rust - self: &Foo - ``` - "#]], + ```rust + self: &Foo // size = 8, align = 8 + ``` + "#]], ); } @@ -3301,12 +3322,12 @@ impl Foo { } "#, expect![[r#" - *self* + *self* - ```rust - self: Arc - ``` - "#]], + ```rust + self: Arc // size = 0, align = 1 + ``` + "#]], ); } @@ -4364,9 +4385,9 @@ fn main() { *tile4* ```rust - let tile4: [u32; 8] + let tile4: [u32; 8] // size = 32, align = 4 ``` - "#]], + "#]], ); } @@ -5541,7 +5562,7 @@ enum Enum { ``` ```rust - RecordV { field: u32 } + RecordV { field: u32 } // size = 4 ``` "#]], ); From 23ce228d545aef4ad7933e69ff9d68961f407605 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 19 May 2023 00:16:52 +0330 Subject: [PATCH 444/806] Reduce MIR memory usage --- crates/hir-ty/src/mir.rs | 40 ++++++++++++++++--- crates/hir-ty/src/mir/borrowck.rs | 4 +- crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir-ty/src/mir/lower.rs | 40 +++++++++---------- crates/hir-ty/src/mir/lower/as_place.rs | 19 +++++---- .../hir-ty/src/mir/lower/pattern_matching.rs | 33 ++++++++------- 6 files changed, 83 insertions(+), 55 deletions(-) diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 11c35dfb8a878..f8451f28d732a 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -205,7 +205,7 @@ type PlaceElem = ProjectionElem; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: Vec, + pub projection: Box<[PlaceElem]>, } impl Place { @@ -216,13 +216,20 @@ impl Place { fn iterate_over_parents(&self) -> impl Iterator + '_ { (0..self.projection.len()) .map(|x| &self.projection[0..x]) - .map(|x| Place { local: self.local, projection: x.to_vec() }) + .map(|x| Place { local: self.local, projection: x.to_vec().into() }) + } + + fn project(&self, projection: PlaceElem) -> Place { + Place { + local: self.local, + projection: self.projection.iter().cloned().chain([projection]).collect(), + } } } impl From for Place { fn from(local: LocalId) -> Self { - Self { local, projection: vec![] } + Self { local, projection: vec![].into() } } } @@ -437,7 +444,7 @@ pub enum TerminatorKind { /// These are owned by the callee, which is free to modify them. /// This allows the memory occupied by "by-value" arguments to be /// reused across function calls without duplicating the contents. - args: Vec, + args: Box<[Operand]>, /// Where the returned value will be written destination: Place, /// Where to go after this call returns. If none, the call necessarily diverges. @@ -894,7 +901,7 @@ pub enum Rvalue { /// /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After /// generator lowering, `Generator` aggregate kinds are disallowed too. - Aggregate(AggregateKind, Vec), + Aggregate(AggregateKind, Box<[Operand]>), /// Transmutes a `*mut u8` into shallow-initialized `Box`. /// @@ -1011,7 +1018,7 @@ impl MirBody { for_operand(o2, &mut f); } Rvalue::Aggregate(_, ops) => { - for op in ops { + for op in ops.iter_mut() { for_operand(op, &mut f); } } @@ -1058,6 +1065,27 @@ impl MirBody { } } } + + fn shrink_to_fit(&mut self) { + let MirBody { + basic_blocks, + locals, + start_block: _, + owner: _, + binding_locals, + param_locals, + closures, + } = self; + basic_blocks.shrink_to_fit(); + locals.shrink_to_fit(); + binding_locals.shrink_to_fit(); + param_locals.shrink_to_fit(); + closures.shrink_to_fit(); + for (_, b) in basic_blocks.iter_mut() { + let BasicBlock { statements, terminator: _, is_cleanup: _ } = b; + statements.shrink_to_fit(); + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 412390d3fa1d4..66af6658e81ad 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -93,7 +93,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec Operand::Copy(p) | Operand::Move(p) => { let mut ty: Ty = body.locals[p.local].ty.clone(); let mut is_dereference_of_ref = false; - for proj in &p.projection { + for proj in &*p.projection { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -143,7 +143,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec for_operand(o2, statement.span); } Rvalue::Aggregate(_, ops) => { - for op in ops { + for op in ops.iter() { for_operand(op, statement.span); } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 327161b99eb3b..e4acd10aa9bea 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -562,7 +562,7 @@ impl Evaluator<'_> { let mut ty: Ty = self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; let mut metadata: Option = None; // locals are always sized - for proj in &p.projection { + for proj in &*p.projection { let prev_ty = ty.clone(); ty = proj.projected_ty( ty, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 292a771baf919..f73b25d83f4e1 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -387,7 +387,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current, place, ty, - vec![], + Box::new([]), expr_id.into(), )?; } @@ -561,7 +561,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; 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, place, label, expr_id.into(), |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, expr_id.into())? + let Some(current) = this.lower_call(iter_next_fn_op, Box::new([Operand::Copy(ref_mut_iterator_place)]), option_item_place.clone(), begin, false, expr_id.into())? else { return Ok(()); }; @@ -758,8 +758,7 @@ impl<'ctx> MirLowerCtx<'ctx> { match x { Some(x) => x, None => { - let mut p = sp.clone(); - p.projection.push(ProjectionElem::Field(FieldId { + let p = sp.project(ProjectionElem::Field(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), })); @@ -782,10 +781,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; 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 })); + let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); self.lower_expr_to_place(*expr, place, current) } } @@ -826,8 +822,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; - let mut p = place; - p.projection.push(ProjectionElem::Deref); + let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) }, @@ -1031,7 +1026,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_assignment( current, place, - Rvalue::Aggregate(AggregateKind::Closure(ty), operands), + Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()), expr_id.into(), ); Ok(Some(current)) @@ -1128,11 +1123,11 @@ impl<'ctx> MirLowerCtx<'ctx> { let index = name .as_tuple_index() .ok_or(MirLowerError::TypeError("named field on tuple"))?; - place.projection.push(ProjectionElem::TupleOrClosureField(index)) + *place = place.project(ProjectionElem::TupleOrClosureField(index)) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - place.projection.push(ProjectionElem::Field(field)); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -1242,7 +1237,7 @@ impl<'ctx> MirLowerCtx<'ctx> { prev_block: BasicBlockId, place: Place, ty: Ty, - fields: Vec, + fields: Box<[Operand]>, span: MirSpan, ) -> Result { let subst = match ty.kind(Interner) { @@ -1280,13 +1275,13 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; - self.lower_call(func, args, place, current, is_uninhabited, span) + self.lower_call(func, args.into(), place, current, is_uninhabited, span) } fn lower_call( &mut self, func: Operand, - args: Vec, + args: Box<[Operand]>, place: Place, current: BasicBlockId, is_uninhabited: bool, @@ -1744,12 +1739,13 @@ pub fn mir_body_for_closure_query( match r { Some(x) => { p.local = closure_local; - let prev_projs = - mem::replace(&mut p.projection, vec![PlaceElem::TupleOrClosureField(x.1)]); + let mut next_projs = vec![PlaceElem::TupleOrClosureField(x.1)]; + let prev_projs = mem::take(&mut p.projection); if x.0.kind != CaptureKind::ByValue { - p.projection.push(ProjectionElem::Deref); + next_projs.push(ProjectionElem::Deref); } - p.projection.extend(prev_projs.into_iter().skip(x.0.place.projections.len())); + next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len())); + p.projection = next_projs.into(); } None => err = Some(p.clone()), } @@ -1764,6 +1760,7 @@ pub fn mir_body_for_closure_query( if let Some(err) = err { return Err(MirLowerError::UnresolvedUpvar(err)); } + ctx.result.shrink_to_fit(); Ok(Arc::new(ctx.result)) } @@ -1780,7 +1777,8 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result { )? else { return Ok(None); }; - x.0.projection.push(ProjectionElem::Deref); + x.0 = x.0.project(ProjectionElem::Deref); Ok(Some(x)) } Adjust::Deref(Some(od)) => { @@ -139,15 +139,14 @@ impl MirLowerCtx<'_> { let ty = self.expr_ty_without_adjust(expr_id); let ref_ty = TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner); - let mut temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp.clone(), Operand::Static(s).into(), expr_id.into(), ); - temp.projection.push(ProjectionElem::Deref); - Ok(Some((temp, current))) + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } @@ -196,7 +195,7 @@ impl MirLowerCtx<'_> { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r.projection.push(ProjectionElem::Deref); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } _ => try_rvalue(self), @@ -253,7 +252,7 @@ impl MirLowerCtx<'_> { 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)); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -283,10 +282,10 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(place), index_operand], result.clone(), current, false, span)? else { + let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else { return Ok(None); }; - result.projection.push(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } @@ -330,10 +329,10 @@ impl MirLowerCtx<'_> { .intern(Interner), ); let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); - let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false, span)? else { + let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else { return Ok(None); }; - result.projection.push(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 45c245e281bc4..00864907ac887 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -110,10 +110,10 @@ impl MirLowerCtx<'_> { Pat::Slice { prefix, slice, suffix } => { pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); for (i, &pat) in prefix.iter().enumerate() { - let mut next_place = cond_place.clone(); - next_place - .projection - .push(ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); let cond_ty = self.infer[pat].clone(); (current, current_else) = self.pattern_match( current, @@ -126,8 +126,7 @@ impl MirLowerCtx<'_> { } if let Some(slice) = slice { if let Pat::Bind { id, subpat: _ } = self.body[*slice] { - let mut next_place = cond_place.clone(); - next_place.projection.push(ProjectionElem::Subslice { + let next_place = cond_place.project(ProjectionElem::Subslice { from: prefix.len() as u64, to: suffix.len() as u64, }); @@ -142,10 +141,10 @@ impl MirLowerCtx<'_> { } } for (i, &pat) in suffix.iter().enumerate() { - let mut next_place = cond_place.clone(); - next_place - .projection - .push(ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); let cond_ty = self.infer[pat].clone(); (current, current_else) = self.pattern_match( current, @@ -269,11 +268,10 @@ impl MirLowerCtx<'_> { Pat::Ref { pat, mutability: _ } => { if let Some((ty, _, _)) = cond_ty.as_reference() { cond_ty = ty.clone(); - cond_place.projection.push(ProjectionElem::Deref); self.pattern_match( current, current_else, - cond_place, + cond_place.project(ProjectionElem::Deref), cond_ty, *pat, binding_mode, @@ -479,8 +477,7 @@ impl MirLowerCtx<'_> { binding_mode: BindingAnnotation, ) -> Result<(BasicBlockId, Option)> { for (proj, arg, ty) in args { - let mut cond_place = cond_place.clone(); - cond_place.projection.push(proj); + let cond_place = cond_place.project(proj); (current, current_else) = self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?; } @@ -513,5 +510,11 @@ fn pattern_matching_dereference( cond_place: &mut Place, ) { let cnt = pattern_matching_dereference_count(cond_ty, binding_mode); - cond_place.projection.extend((0..cnt).map(|_| ProjectionElem::Deref)); + cond_place.projection = cond_place + .projection + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(); } From eaddc3707520988f8bc2d267cb192d2f0c63ee80 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 20 Feb 2023 17:28:03 -0500 Subject: [PATCH 445/806] Take MIR dataflow analyses by mutable reference. --- compiler/rustc_borrowck/src/dataflow.rs | 18 +-- compiler/rustc_borrowck/src/lib.rs | 9 +- .../src/transform/check_consts/resolver.rs | 6 +- .../src/framework/cursor.rs | 112 +++++++++++++++--- .../src/framework/direction.rs | 60 +++++----- .../src/framework/engine.rs | 102 +++++++++++++--- .../src/framework/graphviz.rs | 98 ++++++++------- .../rustc_mir_dataflow/src/framework/mod.rs | 61 ++++++---- .../rustc_mir_dataflow/src/framework/tests.rs | 13 +- .../src/framework/visitor.rs | 45 ++++--- .../src/impls/borrowed_locals.rs | 7 +- .../rustc_mir_dataflow/src/impls/liveness.rs | 18 +-- compiler/rustc_mir_dataflow/src/impls/mod.rs | 28 ++--- .../src/impls/storage_liveness.rs | 87 +++++++------- compiler/rustc_mir_dataflow/src/lib.rs | 5 +- compiler/rustc_mir_dataflow/src/rustc_peek.rs | 23 ++-- .../rustc_mir_dataflow/src/value_analysis.rs | 8 +- .../src/dataflow_const_prop.rs | 60 ++++++---- compiler/rustc_mir_transform/src/generator.rs | 19 +-- 19 files changed, 491 insertions(+), 288 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 167f245361a22..9e0a9a3b6f447 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -59,7 +59,7 @@ macro_rules! impl_visitable { } fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -69,7 +69,7 @@ macro_rules! impl_visitable { } fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -79,7 +79,7 @@ macro_rules! impl_visitable { } fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -89,7 +89,7 @@ macro_rules! impl_visitable { } fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -341,7 +341,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { type Idx = BorrowIndex; fn before_statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -350,7 +350,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, location: Location, @@ -398,7 +398,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn before_terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -407,7 +407,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, _location: Location, @@ -424,7 +424,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index eb25d454339b5..c00efdaa9ac96 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -372,7 +372,7 @@ fn do_mir_borrowck<'tcx>( // Compute and report region errors, if any. mbcx.report_region_errors(nll_errors); - let results = BorrowckResults { + let mut results = BorrowckResults { ever_inits: flow_ever_inits, uninits: flow_uninits, borrows: flow_borrows, @@ -383,7 +383,7 @@ fn do_mir_borrowck<'tcx>( rustc_mir_dataflow::visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), - &results, + &mut results, &mut mbcx, ); @@ -618,11 +618,12 @@ struct MirBorrowckCtxt<'cx, 'tcx> { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { +impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorrowckCtxt<'cx, 'tcx> { type FlowState = Flows<'cx, 'tcx>; fn visit_statement_before_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, stmt: &'cx Statement<'tcx>, location: Location, @@ -692,6 +693,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx fn visit_terminator_before_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, loc: Location, @@ -800,6 +802,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx fn visit_terminator_after_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 78c74e1892dc5..65fe164f8ec76 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -337,7 +337,7 @@ where Q: Qualif, { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -346,7 +346,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -355,7 +355,7 @@ where } fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index f3b5544aa8b9d..c978bddfef540 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,18 +1,60 @@ //! Random access inspection of the results of a dataflow analysis. -use crate::framework::BitSetExt; +use crate::{framework::BitSetExt, CloneAnalysis}; -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; use std::cmp::Ordering; #[cfg(debug_assertions)] use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Effect, EffectIndex, Results}; +use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned}; + +// `AnalysisResults` is needed as an impl such as the following has an unconstrained type +// parameter: +// ``` +// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R> +// where +// A: Analysis<'tcx>, +// E: Borrow>, +// R: Results<'tcx, A, E>, +// {} +// ``` + +/// A type representing the analysis results consumed by a `ResultsCursor`. +pub trait AnalysisResults<'tcx, A>: BorrowMut> +where + A: Analysis<'tcx>, +{ + /// The type containing the entry sets for this `Results` type. + /// + /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`. + type EntrySets: Borrow>; +} +impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow>, +{ + type EntrySets = E; +} +impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow>, +{ + type EntrySets = E; +} /// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; +pub type ResultsRefCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>; + +/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This +/// allows multiple cursors over the same `Results`. +pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>; /// Allows random access inspection of the results of a dataflow analysis. /// @@ -45,7 +87,38 @@ where impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: Analysis<'tcx>, - R: Borrow>, +{ + /// Returns the dataflow state at the current location. + pub fn get(&self) -> &A::Domain { + &self.state + } + + /// Returns the body this analysis was run on. + pub fn body(&self) -> &'mir mir::Body<'tcx> { + self.body + } + + /// Unwraps this cursor, returning the underlying `Results`. + pub fn into_results(self) -> R { + self.results + } +} + +impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not* + /// copied. + pub fn new_cursor(&self) -> Self { + Self::new(self.body, self.results.reclone_analysis()) + } +} + +impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx>, + R: AnalysisResults<'tcx, A>, { /// Returns a new cursor that can inspect `results`. pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { @@ -74,8 +147,13 @@ where } /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A> { - &self.results.borrow() + pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> { + self.results.borrow() + } + + /// Returns the underlying `Results`. + pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> { + self.results.borrow_mut() } /// Returns the `Analysis` used to generate the underlying `Results`. @@ -83,9 +161,14 @@ where &self.results.borrow().analysis } - /// Returns the dataflow state at the current location. - pub fn get(&self) -> &A::Domain { - &self.state + /// Returns the `Analysis` used to generate the underlying `Results`. + pub fn mut_analysis(&mut self) -> &mut A { + &mut self.results.borrow_mut().analysis + } + + /// Returns both the dataflow state at the current location and the `Analysis`. + pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) { + (&self.state, &mut self.results.borrow_mut().analysis) } /// Resets the cursor to hold the entry set for the given basic block. @@ -97,7 +180,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(self.results.borrow().entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -186,7 +269,7 @@ where ) }; - let analysis = &self.results.borrow().analysis; + let analysis = &mut self.results.borrow_mut().analysis; let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( @@ -205,8 +288,8 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { - f(&self.results.borrow().analysis, &mut self.state); + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) { + f(&mut self.results.borrow_mut().analysis, &mut self.state); self.state_needs_reset = true; } } @@ -215,7 +298,6 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: crate::GenKillAnalysis<'tcx>, A::Domain: BitSetExt, - R: Borrow>, { pub fn contains(&self, elem: A::Idx) -> bool { self.get().contains(elem) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index c8fe1af6674c8..f6100a90a0464 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -16,7 +16,7 @@ pub trait Direction { /// /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -25,7 +25,7 @@ pub trait Direction { A: Analysis<'tcx>; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -33,7 +33,7 @@ pub trait Direction { A: Analysis<'tcx>; fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -44,13 +44,13 @@ pub trait Direction { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>; fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -67,7 +67,7 @@ impl Direction for Backward { const IS_FORWARD: bool = false; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -87,7 +87,7 @@ impl Direction for Backward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -107,7 +107,7 @@ impl Direction for Backward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -187,36 +187,36 @@ impl Direction for Backward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_end(&state, block_data, block); + vis.visit_block_end(results, &state, block_data, block); // Terminator let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -319,7 +319,7 @@ impl Direction for Forward { const IS_FORWARD: bool = true; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -339,7 +339,7 @@ impl Direction for Forward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -359,7 +359,7 @@ impl Direction for Forward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -435,35 +435,35 @@ impl Direction for Forward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); - vis.visit_block_end(state, block_data, block); + vis.visit_block_end(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, _body: &mir::Body<'tcx>, exit_state: &mut A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 3e8f792e63442..4bdfa35fc708b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -5,7 +5,9 @@ use crate::errors::{ }; use crate::framework::BitSetExt; +use std::borrow::Borrow; use std::ffi::OsString; +use std::marker::PhantomData; use std::path::PathBuf; use rustc_ast as ast; @@ -22,54 +24,108 @@ use rustc_span::symbol::{sym, Symbol}; use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, - ResultsCursor, ResultsVisitor, + visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis, + GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, + ResultsVisitor, }; +pub type EntrySets<'tcx, A> = IndexVec>::Domain>; + /// A dataflow analysis that has converged to fixpoint. -pub struct Results<'tcx, A> +pub struct Results<'tcx, A, E = EntrySets<'tcx, A>> where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec, + pub(super) entry_sets: E, + pub(super) _marker: PhantomData<&'tcx ()>, } -impl<'tcx, A> Results<'tcx, A> +/// `Results` type with a cloned `Analysis` and borrowed entry sets. +pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>; + +impl<'tcx, A, E> Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow>, { /// Creates a `ResultsCursor` that can inspect these `Results`. pub fn into_results_cursor<'mir>( self, body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { + ) -> ResultsCursor<'mir, 'tcx, A, Self> { ResultsCursor::new(body, self) } /// Gets the dataflow state for the given block. pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_sets[block] + &self.entry_sets.borrow()[block] } pub fn visit_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } pub fn visit_reachable_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn as_results_cursor<'a, 'mir>( + &'a mut self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsRefCursor<'a, 'mir, 'tcx, A> { + ResultsCursor::new(body, self) + } +} +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: &self.entry_sets, + _marker: PhantomData, + } + } + + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn cloned_results_cursor<'mir>( + &self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> { + self.clone_analysis().into_results_cursor(body) + } +} +impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn reclone_analysis(&self) -> Self { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: self.entry_sets, + _marker: PhantomData, + } + } +} /// A solver for dataflow problems. pub struct Engine<'a, 'tcx, A> @@ -98,7 +154,7 @@ where T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. - pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self { + pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut analysis: A) -> Self { // If there are no back-edges in the control-flow graph, we only ever need to apply the // transfer function for each block exactly once (assuming that we process blocks in RPO). // @@ -114,7 +170,7 @@ where for (block, block_data) in body.basic_blocks.iter_enumerated() { let trans = &mut trans_for_block[block]; - A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); + A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data); } let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { @@ -171,7 +227,13 @@ where A::Domain: DebugWithContext, { let Engine { - analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, .. + mut analysis, + body, + mut entry_sets, + tcx, + apply_trans_for_block, + pass_name, + .. } = self; let mut dirty_queue: WorkQueue = WorkQueue::with_none(body.basic_blocks.len()); @@ -203,11 +265,13 @@ where // Apply the block transfer function, using the cached one if it exists. match &apply_trans_for_block { Some(apply) => apply(bb, &mut state), - None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), + None => { + A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data) + } } A::Direction::join_state_into_successors_of( - &analysis, + &mut analysis, tcx, body, &mut state, @@ -221,9 +285,9 @@ where ); } - let results = Results { analysis, entry_sets }; + let mut results = Results { analysis, entry_sets, _marker: PhantomData }; - let res = write_graphviz_results(tcx, &body, &results, pass_name); + let res = write_graphviz_results(tcx, body, &mut results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } @@ -239,7 +303,7 @@ where fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, pass_name: Option<&'static str>, ) -> std::io::Result<()> where diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 707729f8f21b7..e331533c37103 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -1,6 +1,7 @@ //! A helpful diagram for debugging dataflow problems. use std::borrow::Cow; +use std::cell::RefCell; use std::sync::OnceLock; use std::{io, ops, str}; @@ -28,23 +29,27 @@ impl OutputStyle { } } -pub struct Formatter<'a, 'tcx, A> +pub struct Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - body: &'a Body<'tcx>, - results: &'a Results<'tcx, A>, + body: &'mir Body<'tcx>, + results: RefCell<&'res mut Results<'tcx, A>>, style: OutputStyle, reachable: BitSet, } -impl<'a, 'tcx, A> Formatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self { + pub fn new( + body: &'mir Body<'tcx>, + results: &'res mut Results<'tcx, A>, + style: OutputStyle, + ) -> Self { let reachable = mir::traversal::reachable_as_bitset(body); - Formatter { body, results, style, reachable } + Formatter { body, results: results.into(), style, reachable } } } @@ -64,7 +69,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec { .collect() } -impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A> +impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -83,13 +88,14 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); + let mut results = self.results.borrow_mut(); let mut fmt = BlockFormatter { - results: ResultsRefCursor::new(self.body, self.results), + results: results.as_results_cursor(self.body), style: self.style, bg: Background::Light, }; - fmt.write_node_label(&mut label, self.body, *block).unwrap(); + fmt.write_node_label(&mut label, *block).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -103,7 +109,7 @@ where } } -impl<'a, 'tcx, A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -137,16 +143,16 @@ where } } -struct BlockFormatter<'a, 'tcx, A> +struct BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - results: ResultsRefCursor<'a, 'a, 'tcx, A>, + results: ResultsRefCursor<'res, 'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'a, 'tcx, A> BlockFormatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -159,12 +165,7 @@ where bg } - fn write_node_label( - &mut self, - w: &mut impl io::Write, - body: &'a Body<'tcx>, - block: BasicBlock, - ) -> io::Result<()> { + fn write_node_label(&mut self, w: &mut impl io::Write, block: BasicBlock) -> io::Result<()> { // Sample output: // +-+-----------------------------------------------+ // A | bb4 | @@ -215,11 +216,11 @@ where self.write_row_with_full_state(w, "", "(on start)")?; // D + E: Statement and terminator transfer functions - self.write_statements_and_terminator(w, body, block)?; + self.write_statements_and_terminator(w, block)?; // F: State at end of block - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. @@ -389,10 +390,14 @@ where fn write_statements_and_terminator( &mut self, w: &mut impl io::Write, - body: &'a Body<'tcx>, block: BasicBlock, ) -> io::Result<()> { - let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + let diffs = StateDiffCollector::run( + self.results.body(), + block, + self.results.mut_results(), + self.style, + ); let mut diffs_before = diffs.before.map(|v| v.into_iter()); let mut diffs_after = diffs.after.into_iter(); @@ -401,7 +406,7 @@ where if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() } }; - for (i, statement) in body[block].statements.iter().enumerate() { + for (i, statement) in self.results.body()[block].statements.iter().enumerate() { let statement_str = format!("{statement:?}"); let index_str = format!("{i}"); @@ -423,7 +428,7 @@ where assert!(diffs_after.is_empty()); assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty)); - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); let mut terminator_str = String::new(); terminator.kind.fmt_head(&mut terminator_str).unwrap(); @@ -492,29 +497,24 @@ where } } -struct StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - analysis: &'a A, - prev_state: A::Domain, +struct StateDiffCollector { + prev_state: D, before: Option>, after: Vec, } -impl<'a, 'tcx, A> StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, - A::Domain: DebugWithContext, -{ - fn run( - body: &'a mir::Body<'tcx>, +impl StateDiffCollector { + fn run<'tcx, A>( + body: &mir::Body<'tcx>, block: BasicBlock, - results: &'a Results<'tcx, A>, + results: &mut Results<'tcx, A>, style: OutputStyle, - ) -> Self { + ) -> Self + where + A: Analysis<'tcx, Domain = D>, + D: DebugWithContext, + { let mut collector = StateDiffCollector { - analysis: &results.analysis, prev_state: results.analysis.bottom_value(body), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), @@ -525,7 +525,7 @@ where } } -impl<'a, 'tcx, A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> +impl<'tcx, A> ResultsVisitor<'_, 'tcx, Results<'tcx, A>> for StateDiffCollector where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -534,6 +534,7 @@ where fn visit_block_start( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -545,6 +546,7 @@ where fn visit_block_end( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -556,45 +558,49 @@ where fn visit_statement_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_statement_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } fn visit_terminator_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_terminator_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f2263007f6fe9..e5e9c1507ba8b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -45,9 +45,9 @@ pub mod graphviz; pub mod lattice; mod visitor; -pub use self::cursor::{ResultsCursor, ResultsRefCursor}; +pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; -pub use self::engine::{Engine, Results}; +pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; @@ -146,7 +146,7 @@ pub trait AnalysisDomain<'tcx> { pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -159,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_statement_effect`. fn apply_before_statement_effect( - &self, + &mut self, _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -173,7 +173,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -186,7 +186,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_terminator_effect`. fn apply_before_terminator_effect( - &self, + &mut self, _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -201,7 +201,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// This is separate from `apply_terminator_effect` to properly track state across unwind /// edges. fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -214,7 +214,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// By default, no effects happen. fn apply_yield_resume_effect( - &self, + &mut self, _state: &mut Self::Domain, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -235,7 +235,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// engine doesn't need to clone the exit state for a block unless /// `SwitchIntEdgeEffects::apply` is actually called. fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _apply_edge_effects: &mut impl SwitchIntEdgeEffects, @@ -269,6 +269,21 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { } } +/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or +/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new +/// `ResultsCursor` or `ResultsVisitor` +pub trait CloneAnalysis { + fn clone_analysis(&self) -> Self; +} +impl<'tcx, A> CloneAnalysis for A +where + A: Analysis<'tcx> + Copy, +{ + fn clone_analysis(&self) -> Self { + *self + } +} + /// A gen/kill dataflow problem. /// /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only @@ -282,7 +297,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_statement_effect`. fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -290,7 +305,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_statement_effect`. fn before_statement_effect( - &self, + &mut self, _trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, _location: Location, @@ -299,7 +314,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_terminator_effect`. fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -307,7 +322,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_terminator_effect`. fn before_terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -318,7 +333,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_call_return_effect`. fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -326,7 +341,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_yield_resume_effect`. fn yield_resume_effect( - &self, + &mut self, _trans: &mut impl GenKill, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -335,7 +350,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_switch_int_edge_effects`. fn switch_int_edge_effects>( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _edge_effects: &mut impl SwitchIntEdgeEffects, @@ -349,7 +364,7 @@ where A::Domain: GenKill + BitSetExt, { fn apply_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -358,7 +373,7 @@ where } fn apply_before_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -367,7 +382,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -376,7 +391,7 @@ where } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -387,7 +402,7 @@ where /* Edge-specific effects */ fn apply_call_return_effect( - &self, + &mut self, state: &mut A::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -396,7 +411,7 @@ where } fn apply_yield_resume_effect( - &self, + &mut self, state: &mut A::Domain, resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -405,7 +420,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, block: BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 0fed305b930c4..45c2fe55aca9f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -179,7 +179,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -189,7 +189,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -199,7 +199,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -209,7 +209,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -219,7 +219,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_call_return_effect( - &self, + &mut self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -266,7 +266,8 @@ fn test_cursor(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData } + .into_results_cursor(body); cursor.allow_unreachable(); diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 75b4e150a8a33..76a729827813e 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -1,16 +1,18 @@ +use std::borrow::Borrow; + use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Results}; +use super::{Analysis, Direction, EntrySets, Results}; /// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the /// dataflow state at that location. -pub fn visit_results<'mir, 'tcx, F, V>( +pub fn visit_results<'mir, 'tcx, F, R>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - results: &V, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where - V: ResultsVisitable<'tcx, FlowState = F>, + R: ResultsVisitable<'tcx, FlowState = F>, { let mut state = results.new_flow_state(body); @@ -22,15 +24,18 @@ pub fn visit_results<'mir, 'tcx, F, V>( assert!(reachable_blocks.contains(block)); let block_data = &body[block]; - V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); + R::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); } } -pub trait ResultsVisitor<'mir, 'tcx> { +/// A visitor over the results of an `Analysis`. The type parameter `R` is the results type being +/// visited. +pub trait ResultsVisitor<'mir, 'tcx, R> { type FlowState; fn visit_block_start( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -41,6 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `statement_effect`. fn visit_statement_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -51,6 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// statement applied to `state`. fn visit_statement_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -61,6 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `terminator_effect`. fn visit_terminator_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -73,6 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_terminator_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -81,6 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { fn visit_block_end( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -105,37 +115,38 @@ pub trait ResultsVisitable<'tcx> { fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); } -impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> +impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow>, { type FlowState = A::Domain; @@ -146,11 +157,11 @@ where } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.clone_from(&self.entry_set_for_block(block)); + state.clone_from(self.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -159,7 +170,7 @@ where } fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -168,7 +179,7 @@ where } fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -177,7 +188,7 @@ where } fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 92d30f254a6af..b88ed32b687f6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::*; /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// immovable generators. +#[derive(Clone, Copy)] pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { @@ -36,7 +37,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -45,7 +46,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -54,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index aeca0073304ea..56cd18cf7111c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -21,6 +21,7 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi /// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +#[derive(Clone, Copy)] pub struct MaybeLiveLocals; impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { @@ -43,7 +44,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -52,7 +53,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -61,7 +62,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -74,7 +75,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, @@ -216,6 +217,7 @@ impl DefUse { /// This is basically written for dead store elimination and nothing else. /// /// All of the caveats of `MaybeLiveLocals` apply. +#[derive(Clone, Copy)] pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a BitSet, } @@ -248,7 +250,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { fn apply_statement_effect( - &self, + &mut self, trans: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -283,7 +285,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_terminator_effect( - &self, + &mut self, trans: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -292,7 +294,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_call_return_effect( - &self, + &mut self, trans: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -305,7 +307,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_yield_resume_effect( - &self, + &mut self, trans: &mut Self::Domain, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 171db6965ac18..98cec1c676022 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -306,7 +306,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -329,7 +329,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -351,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -372,7 +372,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, @@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -456,7 +456,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -467,7 +467,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -488,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, @@ -562,7 +562,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -573,7 +573,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -584,7 +584,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -627,7 +627,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans), level = "debug")] fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, location: Location, @@ -651,7 +651,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans, _terminator), level = "debug")] fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -672,7 +672,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 463ce083a64fd..666c8d50a8a8c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,10 +1,9 @@ pub use super::*; -use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor}; +use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor}; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use std::borrow::Cow; -use std::cell::RefCell; #[derive(Clone)] pub struct MaybeStorageLive<'a> { @@ -17,6 +16,12 @@ impl<'a> MaybeStorageLive<'a> { } } +impl crate::CloneAnalysis for MaybeStorageLive<'_> { + fn clone_analysis(&self) -> Self { + self.clone() + } +} + impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { type Domain = BitSet; @@ -43,7 +48,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: Location, @@ -56,7 +61,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _: &mir::Terminator<'tcx>, _: Location, @@ -65,7 +70,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -110,7 +115,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: Location, @@ -123,7 +128,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _: &mir::Terminator<'tcx>, _: Location, @@ -132,7 +137,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -141,28 +146,28 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } } -type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; +type BorrowedLocalsResults<'res, 'mir, 'tcx> = + ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a /// given location; i.e. whether its storage can go away without being observed. -pub struct MaybeRequiresStorage<'mir, 'tcx> { - body: &'mir Body<'tcx>, - borrowed_locals: RefCell>, +pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> { + borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>, } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { - pub fn new( - body: &'mir Body<'tcx>, - borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, - ) -> Self { - MaybeRequiresStorage { - body, - borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), - } +impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> { + pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self { + MaybeRequiresStorage { borrowed_locals } + } +} + +impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> { + fn clone_analysis(&self) -> Self { + Self { borrowed_locals: self.borrowed_locals.new_cursor() } } } -impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Domain = BitSet; const NAME: &'static str = "requires_storage"; @@ -181,17 +186,17 @@ impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx } } -impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Idx = Local; fn before_statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, loc: Location, ) { // If a place is borrowed in a statement, it needs storage for that statement. - self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc); + self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc); match &stmt.kind { StatementKind::StorageDead(l) => trans.kill(*l), @@ -218,7 +223,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _: &mir::Statement<'tcx>, loc: Location, @@ -229,13 +234,13 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn before_terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, loc: Location, ) { // If a place is borrowed in a terminator, it needs storage for that terminator. - self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc); + self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc); match &terminator.kind { TerminatorKind::Call { destination, .. } => { @@ -282,7 +287,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, loc: Location, @@ -321,7 +326,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -330,7 +335,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill, _resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -339,28 +344,28 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. - fn check_for_move(&self, trans: &mut impl GenKill, loc: Location) { - let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; - visitor.visit_location(&self.body, loc); + fn check_for_move(&mut self, trans: &mut impl GenKill, loc: Location) { + let body = self.borrowed_locals.body(); + let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals }; + visitor.visit_location(body, loc); } } -struct MoveVisitor<'a, 'mir, 'tcx, T> { - borrowed_locals: &'a RefCell>, +struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> { + borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>, trans: &'a mut T, } -impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T> +impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T> where T: GenKill, { fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) { if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { - let mut borrowed_locals = self.borrowed_locals.borrow_mut(); - borrowed_locals.seek_before_primary_effect(loc); - if !borrowed_locals.contains(local) { + self.borrowed_locals.seek_before_primary_effect(loc); + if !self.borrowed_locals.contains(local) { self.trans.kill(local); } } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index fc4efb943e6b7..3494a37c3cf2d 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -28,8 +28,9 @@ pub use self::drop_flag_effects::{ }; pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces, - Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, + CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, + ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, ResultsVisitable, + ResultsVisitor, SwitchIntEdgeEffects, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 7cae68efbecc3..0cbc7442cf1fb 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -17,7 +17,7 @@ use crate::impls::{ use crate::move_paths::{HasMoveData, MoveData}; use crate::move_paths::{LookupResult, MovePathIndex}; use crate::MoveDataParamEnv; -use crate::{Analysis, JoinSemiLattice, Results, ResultsCursor}; +use crate::{Analysis, JoinSemiLattice, ResultsCursor}; pub struct SanityCheck; @@ -42,7 +42,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_inits); + sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { @@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_uninits); + sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { @@ -58,13 +58,13 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_def_inits); + sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_liveness); + sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { @@ -91,17 +91,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { /// errors are not intended to be used for unit tests.) pub fn sanity_check_via_rustc_peek<'tcx, A>( tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - results: &Results<'tcx, A>, + mut cursor: ResultsCursor<'_, 'tcx, A>, ) where A: RustcPeekAt<'tcx>, { - let def_id = body.source.def_id(); + let def_id = cursor.body().source.def_id(); debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id); - let mut cursor = ResultsCursor::new(body, results); - - let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { + let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call)) }); @@ -132,8 +129,8 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>( ) => { let loc = Location { block: bb, statement_index }; cursor.seek_before_primary_effect(loc); - let state = cursor.get(); - results.analysis.peek_at(tcx, *place, state, call); + let (state, analysis) = cursor.get_with_analysis(); + analysis.peek_at(tcx, *place, state, call); } _ => { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index b74d06e5ae8dd..5693e5a4a712a 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -343,7 +343,7 @@ where T: ValueAnalysis<'tcx>, { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, @@ -354,7 +354,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location, @@ -365,7 +365,7 @@ where } fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, _block: BasicBlock, return_places: crate::CallReturnPlaces<'_, 'tcx>, @@ -376,7 +376,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, discr: &Operand<'tcx>, apply_edge_effects: &mut impl SwitchIntEdgeEffects, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 7adfc9dff2ae9..78fb196358faf 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -10,8 +10,12 @@ use rustc_middle::mir::visit::{MutVisitor, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace}; -use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects}; +use rustc_mir_dataflow::value_analysis::{ + Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, +}; +use rustc_mir_dataflow::{ + lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects, +}; use rustc_span::DUMMY_SP; use rustc_target::abi::{Align, FieldIdx, VariantIdx}; @@ -52,11 +56,11 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp { // Perform the actual dataflow analysis. let analysis = ConstAnalysis::new(tcx, body, map); - let results = debug_span!("analyze") + let mut results = debug_span!("analyze") .in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint()); // Collect results and patch the body afterwards. - let mut visitor = CollectAndPatch::new(tcx, &results.analysis.0.map); + let mut visitor = CollectAndPatch::new(tcx); debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor)); debug_span!("patch").in_scope(|| visitor.visit_body(body)); } @@ -387,9 +391,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } -struct CollectAndPatch<'tcx, 'map> { +struct CollectAndPatch<'tcx> { tcx: TyCtxt<'tcx>, - map: &'map Map, /// For a given MIR location, this stores the values of the operands used by that location. In /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are @@ -400,9 +403,9 @@ struct CollectAndPatch<'tcx, 'map> { assignments: FxHashMap>, } -impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> { - fn new(tcx: TyCtxt<'tcx>, map: &'map Map) -> Self { - Self { tcx, map, before_effect: FxHashMap::default(), assignments: FxHashMap::default() } +impl<'tcx> CollectAndPatch<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() } } fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> { @@ -414,18 +417,23 @@ impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> { } } -impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map> { +impl<'mir, 'tcx> + ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper>>> + for CollectAndPatch<'tcx> +{ type FlowState = State>>; fn visit_statement_before_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, statement: &'mir Statement<'tcx>, location: Location, ) { match &statement.kind { StatementKind::Assign(box (_, rvalue)) => { - OperandCollector { state, visitor: self }.visit_rvalue(rvalue, location); + OperandCollector { state, visitor: self, map: &results.analysis.0.map } + .visit_rvalue(rvalue, location); } _ => (), } @@ -433,6 +441,7 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map fn visit_statement_after_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, statement: &'mir Statement<'tcx>, location: Location, @@ -441,30 +450,34 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(_)))) => { // Don't overwrite the assignment if it already uses a constant (to keep the span). } - StatementKind::Assign(box (place, _)) => match state.get(place.as_ref(), self.map) { - FlatSet::Top => (), - FlatSet::Elem(value) => { - self.assignments.insert(location, value); - } - FlatSet::Bottom => { - // This assignment is either unreachable, or an uninitialized value is assigned. + StatementKind::Assign(box (place, _)) => { + match state.get(place.as_ref(), &results.analysis.0.map) { + FlatSet::Top => (), + FlatSet::Elem(value) => { + self.assignments.insert(location, value); + } + FlatSet::Bottom => { + // This assignment is either unreachable, or an uninitialized value is assigned. + } } - }, + } _ => (), } } fn visit_terminator_before_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, terminator: &'mir Terminator<'tcx>, location: Location, ) { - OperandCollector { state, visitor: self }.visit_terminator(terminator, location); + OperandCollector { state, visitor: self, map: &results.analysis.0.map } + .visit_terminator(terminator, location); } } -impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> { +impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { self.tcx } @@ -496,14 +509,15 @@ impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> { struct OperandCollector<'tcx, 'map, 'a> { state: &'a State>>, - visitor: &'a mut CollectAndPatch<'tcx, 'map>, + visitor: &'a mut CollectAndPatch<'tcx>, + map: &'map Map, } impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { match operand { Operand::Copy(place) | Operand::Move(place) => { - match self.state.get(place.as_ref(), self.visitor.map) { + match self.state.get(place.as_ref(), self.map) { FlatSet::Top => (), FlatSet::Elem(value) => { self.visitor.before_effect.insert((location, *place), value); diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 891e446942e01..98f5020e355c0 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -597,16 +597,15 @@ fn locals_live_across_suspend_points<'tcx>( let borrowed_locals_results = MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint(); - let mut borrowed_locals_cursor = - rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results); + let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body_ref); // Calculate the MIR locals that we actually need to keep storage around // for. - let requires_storage_results = MaybeRequiresStorage::new(body, &borrowed_locals_results) - .into_engine(tcx, body_ref) - .iterate_to_fixpoint(); - let mut requires_storage_cursor = - rustc_mir_dataflow::ResultsCursor::new(body_ref, &requires_storage_results); + let mut requires_storage_results = + MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body)) + .into_engine(tcx, body_ref) + .iterate_to_fixpoint(); + let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body_ref); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = MaybeLiveLocals @@ -747,7 +746,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &GeneratorSavedLocals, always_live_locals: BitSet, - requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, + mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -802,13 +801,14 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> { local_conflicts: BitMatrix, } -impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> +impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> for StorageConflictVisitor<'mir, 'tcx, '_> { type FlowState = BitSet; fn visit_statement_before_primary_effect( &mut self, + _results: &R, state: &Self::FlowState, _statement: &'mir Statement<'tcx>, loc: Location, @@ -818,6 +818,7 @@ impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> fn visit_terminator_before_primary_effect( &mut self, + _results: &R, state: &Self::FlowState, _terminator: &'mir Terminator<'tcx>, loc: Location, From 585783604749ea290083ef5769e82c82b14d83c1 Mon Sep 17 00:00:00 2001 From: Bastiaan Marinus van de Weerd Date: Thu, 18 May 2023 18:30:27 -0400 Subject: [PATCH 446/806] Fix `preorder_expr` skipping the `else` block of let-else statements Fixes exit/yield points not getting highlighted in such blocks for `highlight_related` (#14813; and possibly other bugs in features that use `preorder_expr`). --- crates/ide-db/src/syntax_helpers/node_ext.rs | 5 ++- crates/ide/src/highlight_related.rs | 43 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index a34dc1b69507e..85d39d719ea23 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -52,7 +52,10 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent) } }; if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) { - if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) { + let node = Some(node.clone()); + if node != let_stmt.initializer().map(|it| it.syntax().clone()) + && node != let_stmt.let_else().map(|it| it.syntax().clone()) + { // skipping potential const pat expressions in let statements preorder.skip_subtree(); continue; diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index ff73d5ec9fcf4..7e545491f8e7e 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -692,6 +692,29 @@ pub async$0 fn foo() { ); } + #[test] + fn test_hl_let_else_yield_points() { + check( + r#" +pub async fn foo() { + // ^^^^^ + let x = foo() + .await$0 + // ^^^^^ + .await; + // ^^^^^ + || { 0.await }; + let Some(_) = None else { + foo().await + // ^^^^^ + }; + (async { 0.await }).await + // ^^^^^ +} +"#, + ); + } + #[test] fn test_hl_yield_nested_fn() { check( @@ -788,6 +811,26 @@ async fn foo() { ); } + #[test] + fn test_hl_let_else_exit_points() { + check( + r#" + fn$0 foo() -> u32 { +//^^ + let Some(bar) = None else { + return 0; + // ^^^^^^ + }; + + 0?; + // ^ + 0xDEAD_BEEF + // ^^^^^^^^^^^ +} +"#, + ); + } + #[test] fn test_hl_prefer_ref_over_tail_exit() { check( From c0519daf3b05b66b4878b938f093970825baa730 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 May 2023 09:33:57 +0200 Subject: [PATCH 447/806] Update crates/ide-db/src/syntax_helpers/node_ext.rs Co-authored-by: Bastiaan Marinus van de Weerd --- crates/ide-db/src/syntax_helpers/node_ext.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 85d39d719ea23..22ced69d81e4a 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -52,9 +52,8 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent) } }; if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) { - let node = Some(node.clone()); - if node != let_stmt.initializer().map(|it| it.syntax().clone()) - && node != let_stmt.let_else().map(|it| it.syntax().clone()) + if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true) + && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true) { // skipping potential const pat expressions in let statements preorder.skip_subtree(); From c5ea2d7adc56e645b867687d0290db88e5e5547b Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 19 May 2023 12:00:19 +0330 Subject: [PATCH 448/806] handle match scrutinee in closure captures --- crates/hir-def/src/body.rs | 6 +- crates/hir-ty/src/diagnostics/match_check.rs | 2 +- crates/hir-ty/src/infer.rs | 2 +- crates/hir-ty/src/infer/closure.rs | 71 ++++++++++++++++++- crates/hir-ty/src/infer/pat.rs | 4 +- crates/hir-ty/src/layout/tests/closure.rs | 37 ++++++++++ crates/hir/src/source_analyzer.rs | 4 +- .../src/handlers/moved_out_of_ref.rs | 21 ++++++ 8 files changed, 137 insertions(+), 10 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index ea682f5cbde35..a387bdbc19eba 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -221,15 +221,15 @@ impl Body { pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { self.walk_pats(pat_id, &mut |pat| { - if let Pat::Bind { id, .. } = pat { + if let Pat::Bind { id, .. } = &self[pat] { f(*id); } }); } - pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) { + pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) { let pat = &self[pat_id]; - f(pat); + f(pat_id); match pat { Pat::Range { .. } | Pat::Lit(..) diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index 125df2ba761d9..2b78e7e14fd86 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -147,7 +147,7 @@ impl<'a> PatCtxt<'a> { } hir_def::hir::Pat::Bind { id, subpat, .. } => { - let bm = self.infer.pat_binding_modes[&pat]; + let bm = self.infer.binding_modes[id]; ty = &self.infer[id]; let name = &self.body.bindings[id].name; match (bm, ty.kind(Interner)) { diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 32e870314380a..f01ee1b4e600b 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -389,7 +389,7 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, - pub pat_binding_modes: FxHashMap, + pub binding_modes: ArenaMap, pub expr_adjustments: FxHashMap>, pub(crate) closure_info: FxHashMap, FnTrait)>, // FIXME: remove this field diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 87ec2755d06e5..c3918286f4167 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -504,9 +504,27 @@ impl InferenceContext<'_> { self.consume_exprs(args.iter().copied()); } Expr::Match { expr, arms } => { - self.consume_expr(*expr); for arm in arms.iter() { self.consume_expr(arm.expr); + if let Some(guard) = arm.guard { + self.consume_expr(guard); + } + } + self.walk_expr(*expr); + if let Some(discr_place) = self.place_of_expr(*expr) { + if self.is_upvar(&discr_place) { + let mut capture_mode = None; + for arm in arms.iter() { + self.walk_pat(&mut capture_mode, arm.pat); + } + if let Some(c) = capture_mode { + self.push_capture(CapturedItemWithoutTy { + place: discr_place, + kind: c, + span: (*expr).into(), + }) + } + } } } Expr::Break { expr, label: _ } @@ -618,6 +636,57 @@ impl InferenceContext<'_> { } } + fn walk_pat(&mut self, result: &mut Option, pat: PatId) { + let mut update_result = |ck: CaptureKind| match result { + Some(r) => { + *r = cmp::max(*r, ck); + } + None => *result = Some(ck), + }; + self.body.walk_pats(pat, &mut |p| match &self.body[p] { + Pat::Ref { .. } + | Pat::Box { .. } + | Pat::Missing + | Pat::Wild + | Pat::Tuple { .. } + | Pat::Or(_) => (), + Pat::TupleStruct { .. } | Pat::Record { .. } => { + if let Some(variant) = self.result.variant_resolution_for_pat(p) { + let adt = variant.adt_id(); + let is_multivariant = match adt { + hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1, + _ => false, + }; + if is_multivariant { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + } + } + Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Path(_) + | Pat::Lit(_) + | Pat::Range { .. } => { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + Pat::Bind { id, .. } => match self.result.binding_modes[*id] { + crate::BindingMode::Move => { + if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } else { + update_result(CaptureKind::ByValue); + } + } + crate::BindingMode::Ref(r) => match r { + Mutability::Mut => update_result(CaptureKind::ByRef(BorrowKind::Mut { + allow_two_phase_borrow: false, + })), + Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), + }, + }, + }); + } + fn expr_ty(&self, expr: ExprId) -> Ty { self.result[expr].clone() } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index dd56dc3d9c25a..05f6fcaead26f 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -340,7 +340,7 @@ impl<'a> InferenceContext<'a> { } else { BindingMode::convert(mode) }; - self.result.pat_binding_modes.insert(pat, mode); + self.result.binding_modes.insert(binding, mode); let inner_ty = match subpat { Some(subpat) => self.infer_pat(subpat, &expected, default_bm), @@ -439,7 +439,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { let mut res = false; body.walk_pats(pat_id, &mut |pat| { - res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref); + res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref); }); res } diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs index a2e19852a0742..811d60888074e 100644 --- a/crates/hir-ty/src/layout/tests/closure.rs +++ b/crates/hir-ty/src/layout/tests/closure.rs @@ -182,6 +182,43 @@ fn capture_specific_fields() { } } +#[test] +fn match_pattern() { + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + match y { + _ => x, + } + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + X(_a, _b, _c) => x, + } + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + _y => x, + } + } + } +} + #[test] fn ellipsis_pattern() { size_and_align_expr! { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index dae77fad2f37b..1374fa332c648 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -236,9 +236,9 @@ impl SourceAnalyzer { _db: &dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { - let pat_id = self.pat_id(&pat.clone().into())?; + let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer.as_ref()?; - infer.pat_binding_modes.get(&pat_id).map(|bm| match bm { + infer.binding_modes.get(binding_id).map(|bm| match bm { hir_ty::BindingMode::Move => BindingMode::Move, hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut), hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => { diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 99243a5ab8677..32e321107e6f3 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -151,4 +151,25 @@ fn f(x: &mut X<'_>) { "#, ); } + + #[test] + fn no_false_positive_match_and_closure_capture() { + check_diagnostics( + r#" +//- minicore: copy, fn +enum X { + Foo(u16), + Bar, +} + +fn main() { + let x = &X::Bar; + let c = || match *x { + X::Foo(t) => t, + _ => 5, + }; +} + "#, + ); + } } From 60379dabfb56c1f4cc0dc10c999d81240ed9d998 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 19 May 2023 14:54:57 +0330 Subject: [PATCH 449/806] resolve types in closure capture copy detection --- crates/hir-ty/src/infer/closure.rs | 4 ++-- crates/ide/src/hover/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index c3918286f4167..a7d9246b7cff8 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -710,14 +710,14 @@ impl InferenceContext<'_> { false } - fn is_ty_copy(&self, ty: Ty) -> bool { + fn is_ty_copy(&mut self, ty: Ty) -> bool { if let TyKind::Closure(id, _) = ty.kind(Interner) { // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We // should probably let chalk know which closures are copy, but I don't know how doing it // without creating query cycles. return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true); } - ty.is_copy(self.db, self.owner) + self.table.resolve_completely(ty).is_copy(self.db, self.owner) } fn select_from_expr(&mut self, expr: ExprId) { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 60708cb42ed86..ca6c169348d76 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -301,6 +301,33 @@ fn main() { * `(*x.f2.0.0).f` by mutable borrow "#]], ); + check( + r#" +//- minicore: copy, option + +fn do_char(c: char) {} + +fn main() { + let x = None; + let y = |$0| { + match x { + Some(c) => do_char(c), + None => x = None, + } + }; +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} // size = 8, align = 8 + impl FnMut() + ``` + + ## Captures + * `x` by mutable borrow + "#]], + ); } #[test] From 92d6670f72c3771322485bb29c96900932faf3d6 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 20 May 2023 01:10:37 +0330 Subject: [PATCH 450/806] Consider block impls in `lookup_impl_assoc_item_for_trait_ref` --- crates/hir-ty/src/consteval/tests.rs | 29 ++++++++++++++++++++++++++ crates/hir-ty/src/method_resolution.rs | 12 +++++++++-- crates/ide/src/goto_definition.rs | 23 ++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index b4b5fdd8916c4..b336fa5f0f002 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -503,6 +503,35 @@ fn trait_method() { ); } +#[test] +fn trait_method_inside_block() { + check_number( + r#" +trait Twait { + fn a(&self) -> i32; +} + +fn outer() -> impl Twait { + struct Stwuct; + + impl Twait for Stwuct { + fn a(&self) -> i32 { + 5 + } + } + fn f() -> impl Twait { + let s = Stwuct; + s + } + f() +} + +const GOAL: i32 = outer().a(); + "#, + 5, + ); +} + #[test] fn generic_fn() { check_number( diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index bf1e875858a0f..7208bebb3bd1b 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -729,8 +729,16 @@ fn lookup_impl_assoc_item_for_trait_ref( let self_ty = trait_ref.self_type_parameter(Interner); let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let impls = db.trait_impls_in_deps(env.krate); - let impls = - impls.iter().flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); + let self_impls = match self_ty.kind(Interner) { + TyKind::Adt(id, _) => { + id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x)) + } + _ => None, + }; + let impls = impls + .iter() + .chain(self_impls.as_ref()) + .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); let table = InferenceTable::new(db, env); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 1518b49515301..ef3f14d79d142 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1492,6 +1492,29 @@ impl Twait for Stwuct { fn f() { let s = Stwuct; s.a$0(); +} + "#, + ); + } + #[test] + fn method_call_inside_block() { + check( + r#" +trait Twait { + fn a(&self); +} + +fn outer() { + struct Stwuct; + + impl Twait for Stwuct { + fn a(&self){} + //^ + } + fn f() { + let s = Stwuct; + s.a$0(); + } } "#, ); From 14dc1ac3bf763685e56366c9ad0ae7f60536122b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 20 May 2023 10:38:25 +0200 Subject: [PATCH 451/806] internal: Shrink ProcMacroExpander from 8 to 4 bytes --- crates/hir-expand/src/proc_macro.rs | 23 +++++++++++++---------- crates/hir/src/symbols.rs | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index dc04a41155d1b..c9539210abf65 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct ProcMacroExpander { - proc_macro_id: Option, + proc_macro_id: ProcMacroId, } +const DUMMY_ID: u32 = !0; + impl ProcMacroExpander { pub fn new(proc_macro_id: ProcMacroId) -> Self { - Self { proc_macro_id: Some(proc_macro_id) } + assert_ne!(proc_macro_id.0, DUMMY_ID); + Self { proc_macro_id } } pub fn dummy() -> Self { - Self { proc_macro_id: None } + Self { proc_macro_id: ProcMacroId(DUMMY_ID) } } pub fn is_dummy(&self) -> bool { - self.proc_macro_id.is_none() + self.proc_macro_id.0 == DUMMY_ID } pub fn expand( @@ -32,7 +35,10 @@ impl ProcMacroExpander { attr_arg: Option<&tt::Subtree>, ) -> ExpandResult { match self.proc_macro_id { - Some(id) => { + ProcMacroId(DUMMY_ID) => { + ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate)) + } + ProcMacroId(id) => { let proc_macros = db.proc_macros(); let proc_macros = match proc_macros.get(&def_crate) { Some(Ok(proc_macros)) => proc_macros, @@ -44,13 +50,13 @@ impl ProcMacroExpander { ); } }; - let proc_macro = match proc_macros.get(id.0 as usize) { + let proc_macro = match proc_macros.get(id as usize) { Some(proc_macro) => proc_macro, None => { never!( "Proc macro index out of bounds: the length is {} but the index is {}", proc_macros.len(), - id.0 + id ); return ExpandResult::new( tt::Subtree::empty(), @@ -81,9 +87,6 @@ impl ProcMacroExpander { }, } } - None => { - ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate)) - } } } } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 4eaf99e3e73fa..af37206eadc27 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -50,7 +50,7 @@ impl DeclarationLocation { pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { if let Some(file_id) = self.hir_file_id.file_id() { // fast path to prevent parsing - return Some(FileRange { file_id, range: self.ptr.text_range() }); + return Some(FileRange { file_id, range: self.name_ptr.text_range() }); } let node = resolve_node(db, self.hir_file_id, &self.name_ptr); node.as_ref().original_file_range_opt(db.upcast()) From ca6461c1431dbe043a80fc98f69912b955692d16 Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sat, 20 May 2023 22:29:32 +1000 Subject: [PATCH 452/806] Add proof-of-concept QoS implementation --- Cargo.lock | 6 +- crates/flycheck/Cargo.toml | 1 - crates/flycheck/src/lib.rs | 8 +- crates/ide/src/prime_caches.rs | 6 +- crates/proc-macro-srv/Cargo.toml | 1 + crates/rust-analyzer/Cargo.toml | 1 - crates/rust-analyzer/src/bin/main.rs | 17 +- crates/rust-analyzer/src/task_pool.rs | 25 ++- .../rust-analyzer/tests/slow-tests/support.rs | 4 +- crates/stdx/Cargo.toml | 1 + crates/stdx/src/lib.rs | 1 + crates/stdx/src/thread.rs | 200 ++++++++++++++++++ crates/vfs-notify/Cargo.toml | 2 +- crates/vfs-notify/src/lib.rs | 4 +- 14 files changed, 254 insertions(+), 23 deletions(-) create mode 100644 crates/stdx/src/thread.rs diff --git a/Cargo.lock b/Cargo.lock index 7980823a854fe..e7ae42a2d9b8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,6 @@ dependencies = [ "cargo_metadata", "command-group", "crossbeam-channel", - "jod-thread", "paths", "rustc-hash", "serde", @@ -1278,6 +1277,7 @@ dependencies = [ "paths", "proc-macro-api", "proc-macro-test", + "stdx", "tt", ] @@ -1493,7 +1493,6 @@ dependencies = [ "ide-db", "ide-ssr", "itertools", - "jod-thread", "lsp-server", "lsp-types", "mbe", @@ -1712,6 +1711,7 @@ version = "0.0.0" dependencies = [ "always-assert", "backtrace", + "jod-thread", "libc", "miow", "winapi", @@ -2123,9 +2123,9 @@ name = "vfs-notify" version = "0.0.0" dependencies = [ "crossbeam-channel", - "jod-thread", "notify", "paths", + "stdx", "tracing", "vfs", "walkdir", diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 1e0b3605b1659..3f6671b1c43dd 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -18,7 +18,6 @@ cargo_metadata = "0.15.0" rustc-hash = "1.1.0" serde_json.workspace = true serde.workspace = true -jod-thread = "0.1.2" command-group = "2.0.1" # local deps diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index accb14a51deb9..a4aa346a1cb8a 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig { pub struct FlycheckHandle { // XXX: drop order is significant sender: Sender, - _thread: jod_thread::JoinHandle, + _thread: stdx::thread::JoinHandle, id: usize, } @@ -90,7 +90,7 @@ impl FlycheckHandle { ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); let (sender, receiver) = unbounded::(); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); @@ -395,7 +395,7 @@ struct CargoHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with /// a read syscall dropping and therefore terminating the process is our best option. child: JodGroupChild, - thread: jod_thread::JoinHandle>, + thread: stdx::thread::JoinHandle>, receiver: Receiver, } @@ -409,7 +409,7 @@ impl CargoHandle { let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index 296270036002b..f049a225f077e 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs @@ -80,7 +80,11 @@ pub(crate) fn parallel_prime_caches( for _ in 0..num_worker_threads { let worker = prime_caches_worker.clone(); let db = db.snapshot(); - std::thread::spawn(move || Cancelled::catch(|| worker(db))); + + stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + .allow_leak(true) + .spawn(move || Cancelled::catch(|| worker(db))) + .expect("failed to spawn thread"); } (work_sender, progress_receiver) diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index f7f07cfcb2e27..d5eb157bfef97 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -22,6 +22,7 @@ object = { version = "0.30.2", default-features = false, features = [ libloading = "0.7.3" memmap2 = "0.5.4" +stdx.workspace = true tt.workspace = true mbe.workspace = true paths.workspace = true diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index ae5b8e4c42255..3f795340b2f6b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -86,7 +86,6 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr [dev-dependencies] expect-test = "1.4.0" -jod-thread = "0.1.2" xshell = "0.2.2" test-utils.workspace = true diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 992e174a421f5..660a780eb0383 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -78,7 +78,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { println!("rust-analyzer {}", rust_analyzer::version()); return Ok(()); } - with_extra_thread("LspServer", run_server)?; + with_extra_thread("LspServer", stdx::thread::QoSClass::Utility, run_server)?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, @@ -136,14 +136,17 @@ const STACK_SIZE: usize = 1024 * 1024 * 8; /// space. fn with_extra_thread( thread_name: impl Into, + qos_class: stdx::thread::QoSClass, f: impl FnOnce() -> Result<()> + Send + 'static, ) -> Result<()> { - let handle = - std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?; - match handle.join() { - Ok(res) => res, - Err(panic) => std::panic::resume_unwind(panic), - } + let handle = stdx::thread::Builder::new(qos_class) + .name(thread_name.into()) + .stack_size(STACK_SIZE) + .spawn(f)?; + + handle.join()?; + + Ok(()) } fn run_server() -> Result<()> { diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index 616e449984ae6..0c5a4f3055347 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -1,5 +1,7 @@ //! A thin wrapper around `ThreadPool` to make sure that we join all things //! properly. +use std::sync::{Arc, Barrier}; + use crossbeam_channel::Sender; pub(crate) struct TaskPool { @@ -16,6 +18,18 @@ impl TaskPool { .thread_stack_size(STACK_SIZE) .num_threads(threads) .build(); + + // Set QoS of all threads in threadpool. + let barrier = Arc::new(Barrier::new(threads + 1)); + for _ in 0..threads { + let barrier = barrier.clone(); + inner.execute(move || { + stdx::thread::set_current_thread_qos_class(stdx::thread::QoSClass::Utility); + barrier.wait(); + }); + } + barrier.wait(); + TaskPool { sender, inner } } @@ -26,7 +40,16 @@ impl TaskPool { { self.inner.execute({ let sender = self.sender.clone(); - move || sender.send(task()).unwrap() + move || { + if stdx::thread::IS_QOS_AVAILABLE { + debug_assert_eq!( + stdx::thread::get_current_thread_qos_class(), + Some(stdx::thread::QoSClass::Utility) + ); + } + + sender.send(task()).unwrap() + } }) } diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index d0eeee189c51c..33d7f6576c3cd 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -155,7 +155,7 @@ pub(crate) fn project(fixture: &str) -> Server { pub(crate) struct Server { req_id: Cell, messages: RefCell>, - _thread: jod_thread::JoinHandle<()>, + _thread: stdx::thread::JoinHandle, client: Connection, /// XXX: remove the tempdir last dir: TestDir, @@ -165,7 +165,7 @@ impl Server { fn new(dir: TestDir, config: Config) -> Server { let (connection, client) = Connection::memory(); - let _thread = jod_thread::Builder::new() + let _thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("test server".to_string()) .spawn(move || main_loop(config, connection).unwrap()) .expect("failed to spawn a thread"); diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index c881f2fd3f45e..986e3fcdcfc39 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -15,6 +15,7 @@ doctest = false libc = "0.2.135" backtrace = { version = "0.3.65", optional = true } always-assert = { version = "0.1.2", features = ["log"] } +jod-thread = "0.1.2" # Think twice before adding anything here [target.'cfg(windows)'.dependencies] diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 8df86e810047f..24990d6a0e714 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -11,6 +11,7 @@ pub mod process; pub mod panic_context; pub mod non_empty_vec; pub mod rand; +pub mod thread; pub use always_assert::{always, never}; diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs new file mode 100644 index 0000000000000..2bf9141cbf99b --- /dev/null +++ b/crates/stdx/src/thread.rs @@ -0,0 +1,200 @@ +//! A utility module for working with threads that automatically joins threads upon drop +//! and provides functionality for interfacing with operating system quality of service (QoS) APIs. +//! +//! As a system, rust-analyzer should have the property that +//! old manual scheduling APIs are replaced entirely by QoS. +//! To maintain this invariant, we panic when it is clear that +//! old scheduling APIs have been used. +//! +//! Moreover, we also want to ensure that every thread has a QoS set explicitly +//! to force a decision about its importance to the system. +//! Thus, [`QoSClass`] has no default value +//! and every entry point to creating a thread requires a [`QoSClass`] upfront. + +use std::fmt; + +pub fn spawn(qos_class: QoSClass, f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new(qos_class).spawn(f).expect("failed to spawn thread") +} + +pub struct Builder { + qos_class: QoSClass, + inner: jod_thread::Builder, + allow_leak: bool, +} + +impl Builder { + pub fn new(qos_class: QoSClass) -> Builder { + Builder { qos_class, inner: jod_thread::Builder::new(), allow_leak: false } + } + + pub fn name(self, name: String) -> Builder { + Builder { inner: self.inner.name(name), ..self } + } + + pub fn stack_size(self, size: usize) -> Builder { + Builder { inner: self.inner.stack_size(size), ..self } + } + + pub fn allow_leak(self, b: bool) -> Builder { + Builder { allow_leak: b, ..self } + } + + pub fn spawn(self, f: F) -> std::io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + let inner_handle = self.inner.spawn(move || { + set_current_thread_qos_class(self.qos_class); + f() + })?; + + Ok(JoinHandle { inner: Some(inner_handle), allow_leak: self.allow_leak }) + } +} + +pub struct JoinHandle { + // `inner` is an `Option` so that we can + // take ownership of the contained `JoinHandle` + // in the `Drop` impl below. + inner: Option>, + allow_leak: bool, +} + +impl JoinHandle { + pub fn join(mut self) -> T { + self.inner.take().unwrap().join() + } +} + +impl Drop for JoinHandle { + fn drop(&mut self) { + if !self.allow_leak { + return; + } + + if let Some(join_handle) = self.inner.take() { + join_handle.detach(); + } + } +} + +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum QoSClass { + // Maintain order in priority from least to most. + Background, + Utility, + UserInitiated, + UserInteractive, +} + +#[cfg(target_vendor = "apple")] +pub const IS_QOS_AVAILABLE: bool = true; + +#[cfg(not(target_vendor = "apple"))] +pub const IS_QOS_AVAILABLE: bool = false; + +// All Apple platforms use XNU as their kernel +// and thus have the concept of QoS. +#[cfg(target_vendor = "apple")] +pub fn set_current_thread_qos_class(class: QoSClass) { + let c = match class { + QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, + QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, + QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, + QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, + }; + + let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; + + if code == 0 { + return; + } + + let errno = unsafe { *libc::__error() }; + + match errno { + libc::EPERM => { + // This thread has been excluded from the QoS system + // due to a previous call to a function such as `pthread_setschedparam` + // which is incompatible with QoS. + // + // Let’s just panic here because rust-analyzer as a system + // should have the property that QoS is used consistently + // instead of old manual scheduling management APIs. + panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") + } + + libc::EINVAL => { + // This is returned if we pass something other than a qos_class_t + // to `pthread_set_qos_class_self_np`. + // + // This is impossible, so again panic. + unreachable!("invalid qos_class_t value was passed to pthread_set_qos_class_self_np") + } + + _ => { + // `pthread_set_qos_class_self_np`’s documentation + // does not mention any other errors. + unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") + } + } +} + +#[cfg(not(target_vendor = "apple"))] +pub fn set_current_thread_qos_class(class: QoSClass) { + // FIXME: Windows has QoS APIs, we should use them! +} + +#[cfg(target_vendor = "apple")] +pub fn get_current_thread_qos_class() -> Option { + let current_thread = unsafe { libc::pthread_self() }; + let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; + let code = unsafe { + libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) + }; + + if code != 0 { + // `pthread_get_qos_class_np`’s documentation states that + // an error value is placed into errno if the return code is not zero. + // However, it never states what errors are possible. + // Inspecting the source[0] shows that, as of this writing, it always returns zero. + // + // Whatever errors the function could report in future are likely to be + // ones which we cannot handle anyway + // + // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 + let errno = unsafe { *libc::__error() }; + unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); + } + + match qos_class_raw { + libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), + libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), + libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set + libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), + libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), + libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { + // We panic here because rust-analyzer should never use + panic!("tried to get QoS of thread which has opted out of QoS") + } + } +} + +#[cfg(not(target_vendor = "apple"))] +pub fn get_current_thread_qos_class() -> Option { + None +} diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index e06b98d8118b6..5d61a227284ed 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -13,10 +13,10 @@ doctest = false [dependencies] tracing = "0.1.35" -jod-thread = "0.1.2" walkdir = "2.3.2" crossbeam-channel = "0.5.5" notify = "5.0" +stdx.workspace = true vfs.workspace = true paths.workspace = true diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index c95304e55ac10..26f7a9fc42359 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -21,7 +21,7 @@ use walkdir::WalkDir; pub struct NotifyHandle { // Relative order of fields below is significant. sender: Sender, - _thread: jod_thread::JoinHandle, + _thread: stdx::thread::JoinHandle, } #[derive(Debug)] @@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle { fn spawn(sender: loader::Sender) -> NotifyHandle { let actor = NotifyActor::new(sender); let (sender, receiver) = unbounded::(); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("VfsLoader".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); From b76b0aeb63948fecca6d6f603f47c6648059048a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 20 May 2023 15:39:26 +0200 Subject: [PATCH 453/806] Merge commit '435a8ad86c7a33bd7ffb91c59039943408d3b6aa' into clippyup --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 10 +- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/deploy.yml | 4 +- .github/workflows/remark.yml | 4 +- CHANGELOG.md | 4 + README.md | 2 +- book/src/development/type_checking.md | 6 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/attrs.rs | 144 +++++++++++- clippy_lints/src/borrow_deref_ref.rs | 6 +- clippy_lints/src/box_default.rs | 9 +- clippy_lints/src/casts/cast_nan_to_int.rs | 4 +- .../src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/dbg_macro.rs | 57 ++++- clippy_lints/src/declared_lints.rs | 5 +- .../src/default_constructed_unit_structs.rs | 5 +- clippy_lints/src/floating_point_arithmetic.rs | 22 +- clippy_lints/src/fn_null_check.rs | 6 +- clippy_lints/src/implicit_saturating_add.rs | 4 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 8 +- clippy_lints/src/let_underscore.rs | 24 +- clippy_lints/src/lib.rs | 24 +- clippy_lints/src/lines_filter_map_ok.rs | 2 +- clippy_lints/src/manual_let_else.rs | 61 +++-- clippy_lints/src/manual_strip.rs | 2 +- .../src/matches/match_like_matches.rs | 22 +- clippy_lints/src/matches/match_same_arms.rs | 3 +- clippy_lints/src/matches/mod.rs | 13 +- clippy_lints/src/matches/overlapping_arms.rs | 4 +- .../src/matches/redundant_pattern_match.rs | 215 ++++++++++++------ clippy_lints/src/methods/iter_nth_zero.rs | 2 +- .../src/methods/iterator_step_by_zero.rs | 2 +- clippy_lints/src/methods/manual_next_back.rs | 38 ++++ clippy_lints/src/methods/mod.rs | 37 ++- clippy_lints/src/methods/needless_collect.rs | 68 +++++- clippy_lints/src/methods/repeat_once.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/misc.rs | 11 +- clippy_lints/src/needless_bool.rs | 5 +- .../operators/absurd_extreme_comparisons.rs | 2 +- .../src/operators/arithmetic_side_effects.rs | 12 +- clippy_lints/src/operators/bit_mask.rs | 2 +- clippy_lints/src/operators/cmp_nan.rs | 2 +- clippy_lints/src/operators/duration_subsec.rs | 2 +- clippy_lints/src/operators/float_cmp.rs | 57 ++--- clippy_lints/src/operators/mod.rs | 27 --- .../src/operators/modulo_arithmetic.rs | 6 +- .../src/operators/numeric_arithmetic.rs | 45 +--- clippy_lints/src/option_if_let_else.rs | 12 +- clippy_lints/src/ranges.rs | 8 +- clippy_lints/src/ref_patterns.rs | 44 ++++ clippy_lints/src/regex.rs | 25 +- clippy_lints/src/renamed_lints.rs | 1 + clippy_lints/src/strings.rs | 2 +- clippy_lints/src/trait_bounds.rs | 59 ++++- .../src/transmute/transmute_null_to_fn.rs | 4 +- .../src/transmute/transmuting_null.rs | 5 +- clippy_lints/src/useless_conversion.rs | 2 + clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 5 +- .../internal_lints/metadata_collector.rs | 111 +-------- clippy_lints/src/utils/mod.rs | 140 ++++++++++++ clippy_lints/src/vec.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 6 +- clippy_utils/src/consts.rs | 110 ++++++--- clippy_utils/src/hir_utils.rs | 206 +++++++++++++---- clippy_utils/src/lib.rs | 50 ++-- clippy_utils/src/source.rs | 52 ++++- rust-toolchain | 2 +- tests/ui-internal/custom_ice_message.rs | 1 + tests/ui-internal/custom_ice_message.stderr | 8 +- tests/ui/arithmetic_side_effects.rs | 8 + tests/ui/borrow_deref_ref.fixed | 15 ++ tests/ui/borrow_deref_ref.rs | 15 ++ tests/ui/borrow_deref_ref.stderr | 6 +- tests/ui/box_default.fixed | 7 + tests/ui/box_default.rs | 7 + tests/ui/box_default.stderr | 8 +- tests/ui/collapsible_if.fixed | 7 +- tests/ui/collapsible_if.rs | 7 +- tests/ui/collapsible_if.stderr | 18 +- tests/ui/dbg_macro.rs | 27 +++ tests/ui/dbg_macro.stderr | 82 +++++-- .../ui/default_constructed_unit_structs.fixed | 18 ++ tests/ui/default_constructed_unit_structs.rs | 18 ++ .../default_constructed_unit_structs.stderr | 10 +- tests/ui/empty_line_after_doc_comments.rs | 132 +++++++++++ tests/ui/empty_line_after_doc_comments.stderr | 36 +++ tests/ui/float_arithmetic.rs | 2 +- tests/ui/integer_arithmetic.rs | 109 --------- tests/ui/integer_arithmetic.stderr | 169 -------------- tests/ui/let_underscore_untyped.rs | 15 ++ tests/ui/let_underscore_untyped.stderr | 20 +- tests/ui/manual_let_else.rs | 15 ++ tests/ui/manual_let_else.stderr | 82 ++++--- tests/ui/manual_let_else_match.stderr | 4 +- tests/ui/manual_next_back.fixed | 36 +++ tests/ui/manual_next_back.rs | 36 +++ tests/ui/manual_next_back.stderr | 16 ++ tests/ui/match_expr_like_matches_macro.fixed | 2 +- tests/ui/match_expr_like_matches_macro.stderr | 8 +- tests/ui/match_same_arms.rs | 82 ++++++- tests/ui/match_same_arms2.rs | 6 + tests/ui/match_same_arms2.stderr | 17 +- tests/ui/needless_bool/fixable.fixed | 7 + tests/ui/needless_bool/fixable.rs | 7 + tests/ui/needless_bool/fixable.stderr | 24 +- tests/ui/needless_collect.fixed | 12 + tests/ui/needless_collect.rs | 12 + tests/ui/needless_collect.stderr | 26 ++- tests/ui/needless_return.fixed | 5 +- tests/ui/needless_return.rs | 5 +- tests/ui/needless_return.stderr | 26 +-- tests/ui/non_minimal_cfg.fixed | 17 ++ tests/ui/non_minimal_cfg.rs | 17 ++ tests/ui/non_minimal_cfg.stderr | 28 +++ tests/ui/non_minimal_cfg2.rs | 6 + tests/ui/non_minimal_cfg2.stderr | 10 + tests/ui/option_if_let_else.fixed | 10 + tests/ui/option_if_let_else.rs | 10 + tests/ui/option_if_let_else.stderr | 39 +++- tests/ui/partialeq_to_none.fixed | 1 + tests/ui/partialeq_to_none.rs | 1 + tests/ui/partialeq_to_none.stderr | 30 +-- .../redundant_pattern_matching_option.fixed | 19 ++ tests/ui/redundant_pattern_matching_option.rs | 31 +++ .../redundant_pattern_matching_option.stderr | 60 ++++- .../redundant_pattern_matching_result.fixed | 26 +++ tests/ui/redundant_pattern_matching_result.rs | 38 ++++ .../redundant_pattern_matching_result.stderr | 62 +++-- tests/ui/ref_patterns.rs | 19 ++ tests/ui/ref_patterns.stderr | 27 +++ tests/ui/regex.rs | 2 + tests/ui/regex.stderr | 40 ++-- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 102 +++++---- tests/ui/trait_duplication_in_bounds.fixed | 10 + tests/ui/trait_duplication_in_bounds.rs | 10 + tests/ui/trait_duplication_in_bounds.stderr | 24 +- tests/ui/useless_conversion.fixed | 6 + tests/ui/useless_conversion.rs | 6 + tests/ui/useless_conversion.stderr | 32 +-- tests/ui/wildcard_imports_cfgtest.rs | 19 ++ triagebot.toml | 2 +- 149 files changed, 2623 insertions(+), 1114 deletions(-) create mode 100644 clippy_lints/src/methods/manual_next_back.rs create mode 100644 clippy_lints/src/ref_patterns.rs create mode 100644 tests/ui/empty_line_after_doc_comments.rs create mode 100644 tests/ui/empty_line_after_doc_comments.stderr delete mode 100644 tests/ui/integer_arithmetic.rs delete mode 100644 tests/ui/integer_arithmetic.stderr create mode 100644 tests/ui/manual_next_back.fixed create mode 100644 tests/ui/manual_next_back.rs create mode 100644 tests/ui/manual_next_back.stderr create mode 100644 tests/ui/non_minimal_cfg.fixed create mode 100644 tests/ui/non_minimal_cfg.rs create mode 100644 tests/ui/non_minimal_cfg.stderr create mode 100644 tests/ui/non_minimal_cfg2.rs create mode 100644 tests/ui/non_minimal_cfg2.stderr create mode 100644 tests/ui/ref_patterns.rs create mode 100644 tests/ui/ref_patterns.stderr create mode 100644 tests/ui/wildcard_imports_cfgtest.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index b992130119713..a9d42159c4bac 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -39,7 +39,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 93198aabdb5f5..30a156c925b07 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -27,7 +27,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 with: ref: ${{ github.ref }} @@ -83,7 +83,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -149,7 +149,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -173,7 +173,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -233,7 +233,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 14f20212adda5..514706d64c8c0 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -25,7 +25,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 71d71d10359e7..f42928c2cd116 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 0bc2f49f5e9b5..7d25b6a2b79e7 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,10 +16,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v1.4.4 + uses: actions/setup-node@v3 with: node-version: '14.x' diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf5b58a58699..79f2a47110b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4620,6 +4620,7 @@ Released 2018-09-13 [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum +[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop [`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets @@ -4785,6 +4786,7 @@ Released 2018-09-13 [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy +[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains @@ -4897,6 +4899,7 @@ Released 2018-09-13 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool @@ -4978,6 +4981,7 @@ Released 2018-09-13 [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref +[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts diff --git a/README.md b/README.md index 6745e15c00657..d712d3e675074 100644 --- a/README.md +++ b/README.md @@ -278,7 +278,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT -Copyright 2014-2022 The Rust Project Developers +Copyright 2014-2023 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/book/src/development/type_checking.md b/book/src/development/type_checking.md index 225de84956625..d7c2775b89690 100644 --- a/book/src/development/type_checking.md +++ b/book/src/development/type_checking.md @@ -133,7 +133,7 @@ in this chapter: - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) -[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt +[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt [AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type @@ -142,9 +142,9 @@ in this chapter: [kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html [hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 37e1e6a742f60..98e69c7fd264a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ if_chain = "1.0" itertools = "0.10.1" pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" -regex-syntax = "0.6" +regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } tempfile = { version = "3.2", optional = true } diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a36df55d0bdaf..a8dc0cb3b5815 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, }; let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; - let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return }; if val { span_lint_and_help( cx, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 751c262673b1c..897495ba10874 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -176,6 +176,52 @@ declare_clippy_lint! { "empty line after outer attribute" } +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after documenation comments. + /// + /// ### Why is this bad? + /// The documentation comment was most likely meant to be an inner attribute or regular comment. + /// If it was intended to be a documentation comment, then the empty line should be removed to + /// be more idiomatic. + /// + /// ### Known problems + /// Only detects empty lines immediately following the documentation. If the doc comment is followed + /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` + /// in combination with this lint to detect both cases. + /// + /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). + /// + /// ### Example + /// ```rust + /// /// Some doc comment with a blank line after it. + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```rust + /// /// Good (no blank line) + /// fn this_is_fine() { } + /// ``` + /// + /// ```rust + /// // Good (convert to a regular comment) + /// + /// fn this_is_fine_too() { } + /// ``` + /// + /// ```rust + /// //! Good (convert to a comment on an inner attribute) + /// + /// fn this_is_fine_as_well() { } + /// ``` + #[clippy::version = "1.70.0"] + pub EMPTY_LINE_AFTER_DOC_COMMENTS, + nursery, + "empty line after documentation comments" +} + declare_clippy_lint! { /// ### What it does /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. @@ -292,6 +338,30 @@ declare_clippy_lint! { "ensures that all `allow` and `expect` attributes have a reason" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `any` and `all` combinators in `cfg` with only one condition. + /// + /// ### Why is this bad? + /// If there is only one condition, no need to wrap it into `any` or `all` combinators. + /// + /// ### Example + /// ```rust + /// #[cfg(any(unix))] + /// pub struct Bar; + /// ``` + /// + /// Use instead: + /// ```rust + /// #[cfg(unix)] + /// pub struct Bar; + /// ``` + #[clippy::version = "1.71.0"] + pub NON_MINIMAL_CFG, + style, + "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" +} + declare_lint_pass!(Attributes => [ ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, @@ -604,6 +674,8 @@ impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS, EMPTY_LINE_AFTER_OUTER_ATTR, + EMPTY_LINE_AFTER_DOC_COMMENTS, + NON_MINIMAL_CFG, ]); impl EarlyLintPass for EarlyAttributes { @@ -614,15 +686,22 @@ impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); + check_minimal_cfg_condition(cx, attr); } extract_msrv_attr!(EarlyContext); } +/// Check for empty lines after outer attributes. +/// +/// Attributes and documenation comments are both considered outer attributes +/// by the AST. However, the average user likely considers them to be different. +/// Checking for empty lines after each of these attributes is split into two different +/// lints but can share the same logic. fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { let mut iter = item.attrs.iter().peekable(); while let Some(attr) = iter.next() { - if matches!(attr.kind, AttrKind::Normal(..)) + if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) && attr.style == AttrStyle::Outer && is_present_in_source(cx, attr.span) { @@ -639,13 +718,20 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It let lines = without_block_comments(lines); if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - span_lint( - cx, - EMPTY_LINE_AFTER_OUTER_ATTR, - begin_of_attr_to_item, - "found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - ); + let (lint_msg, lint_type) = match attr.kind { + AttrKind::DocComment(..) => ( + "found an empty line after a doc comment. \ + Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", + EMPTY_LINE_AFTER_DOC_COMMENTS, + ), + AttrKind::Normal(..) => ( + "found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + EMPTY_LINE_AFTER_OUTER_ATTR, + ), + }; + + span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); } } } @@ -690,6 +776,48 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr } } +fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { + for item in items.iter() { + if let NestedMetaItem::MetaItem(meta) = item { + if !meta.has_name(sym::any) && !meta.has_name(sym::all) { + continue; + } + if let MetaItemKind::List(list) = &meta.kind { + check_nested_cfg(cx, list); + if list.len() == 1 { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is only one condition", + |diag| { + if let Some(snippet) = snippet_opt(cx, list[0].span()) { + diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); + } + }, + ); + } else if list.is_empty() && meta.has_name(sym::all) { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is no condition", + |_| {}, + ); + } + } + } + } +} + +fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) { + if attr.has_name(sym::cfg) && + let Some(items) = attr.meta_item_list() + { + check_nested_cfg(cx, &items); + } +} + fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { fn find_os(name: &str) -> Option<&'static str> { UNIX_SYSTEMS diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index c4520d003928e..814108ed8a7c2 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -1,5 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_expr, is_lint_allowed}; @@ -47,8 +48,8 @@ declare_clippy_lint! { declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); -impl LateLintPass<'_> for BorrowDerefRef { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { if_chain! { if !e.span.from_expansion(); if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; @@ -58,6 +59,7 @@ impl LateLintPass<'_> for BorrowDerefRef { if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); let ref_ty = cx.typeck_results().expr_ty(deref_target); if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); + if !is_from_proc_macro(cx, e); then{ if let Some(parent_expr) = get_parent_expr(cx, e){ diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index dfa949d1af2f4..e42c3fe243256 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -8,7 +8,9 @@ use rustc_hir::{ Block, Expr, ExprKind, Local, Node, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_middle::ty::IsSuggestable; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -49,7 +51,6 @@ impl LateLintPass<'_> for BoxDefault { && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { - let arg_ty = cx.typeck_results().expr_ty(arg); span_lint_and_sugg( cx, BOX_DEFAULT, @@ -58,8 +59,10 @@ impl LateLintPass<'_> for BoxDefault { "try", if is_plain_default(arg_path) || given_type(cx, expr) { "Box::default()".into() - } else { + } else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) { with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) + } else { + return }, Applicability::MachineApplicable ); diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs index 322dc41b3a197..da756129db3ae 100644 --- a/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -21,8 +21,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { match constant(cx, cx.typeck_results(), e) { - Some((Constant::F64(n), _)) => n.is_nan(), - Some((Constant::F32(n), _)) => n.is_nan(), + Some(Constant::F64(n)) => n.is_nan(), + Some(Constant::F32(n)) => n.is_nan(), _ => false, } } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 95c2ecbf791b5..84b99ad5c243d 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { Some(c) } else { None diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a20a97d4e56da..a83dfd94dc226 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -29,7 +29,7 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast // Don't lint for positive constants. let const_val = constant(cx, cx.typeck_results(), cast_op); if_chain! { - if let Some((Constant::Int(n), _)) = const_val; + if let Some(Constant::Int(n)) = const_val; if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 799e71e847a9f..ea17e7a607104 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -3,10 +3,10 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Expr, ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::sym; +use rustc_span::{sym, BytePos, Pos, Span}; declare_clippy_lint! { /// ### What it does @@ -31,6 +31,31 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } +/// Gets the span of the statement up to the next semicolon, if and only if the next +/// non-whitespace character actually is a semicolon. +/// E.g. +/// ```rust,ignore +/// +/// dbg!(); +/// ^^^^^^^ this span is returned +/// +/// foo!(dbg!()); +/// no span is returned +/// ``` +fn span_including_semi(cx: &LateContext<'_>, span: Span) -> Option { + let sm = cx.sess().source_map(); + let sf = sm.lookup_source_file(span.hi()); + let src = sf.src.as_ref()?.get(span.hi().to_usize()..)?; + let first_non_whitespace = src.find(|c: char| !c.is_whitespace())?; + + if src.as_bytes()[first_non_whitespace] == b';' { + let hi = span.hi() + BytePos::from_usize(first_non_whitespace + 1); + Some(span.with_hi(hi)) + } else { + None + } +} + #[derive(Copy, Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, @@ -55,13 +80,25 @@ impl LateLintPass<'_> for DbgMacro { return; } let mut applicability = Applicability::MachineApplicable; - let suggestion = match expr.peel_drop_temps().kind { + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { // dbg!() - ExprKind::Block(_, _) => String::new(), - // dbg!(1) - ExprKind::Match(val, ..) => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Some(Node::Stmt(stmt)) = cx.tcx.hir().find_parent(expr.hir_id) + && let Some(span) = span_including_semi(cx, stmt.span.source_callsite()) + { + (span, String::new()) + } else { + (macro_call.span, String::from("()")) + } }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), + ), // dbg!(2, 3) ExprKind::Tup( [ @@ -82,7 +119,7 @@ impl LateLintPass<'_> for DbgMacro { "..", &mut applicability, ); - format!("({snippet})") + (macro_call.span, format!("({snippet})")) }, _ => return, }; @@ -90,7 +127,7 @@ impl LateLintPass<'_> for DbgMacro { span_lint_and_sugg( cx, DBG_MACRO, - macro_call.span, + sugg_span, "the `dbg!` macro is intended as a debugging tool", "remove the invocation before committing it to a version control system", suggestion, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 04993e4928799..423eee47742e0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -48,9 +48,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, crate::attrs::DEPRECATED_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::USELESS_ATTRIBUTE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, @@ -347,6 +349,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, crate::methods::MANUAL_SPLIT_ONCE_INFO, @@ -485,7 +488,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, crate::operators::IDENTITY_OP_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, - crate::operators::INTEGER_ARITHMETIC_INFO, crate::operators::INTEGER_DIVISION_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MODULO_ARITHMETIC_INFO, @@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::redundant_slicing::REDUNDANT_SLICING_INFO, crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, crate::ref_option_ref::REF_OPTION_REF_INFO, + crate::ref_patterns::REF_PATTERNS_INFO, crate::reference::DEREF_ADDROF_INFO, crate::regex::INVALID_REGEX_INFO, crate::regex::TRIVIAL_REGEX_INFO, diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index e529d81a7e9f3..9bd7a0dc0f3b8 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, match_def_path, paths}; +use clippy_utils::{diagnostics::span_lint_and_sugg, match_def_path, paths}; use hir::{def::Res, ExprKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -55,7 +55,8 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind(); if def.is_struct(); if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant(); - if !var.is_field_list_non_exhaustive() && !is_from_proc_macro(cx, expr); + if !var.is_field_list_non_exhaustive(); + if !expr.span.from_expansion() && !qpath.span().from_expansion(); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index a1a2c398a8a02..3c55a563af455 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -114,7 +114,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some((value, _)) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = constant(cx, cx.typeck_results(), base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -193,8 +193,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { constant(cx, cx.typeck_results(), lhs), constant(cx, cx.typeck_results(), rhs), ) { - (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs, - (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs, + (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, + (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, }; @@ -237,7 +237,7 @@ fn get_integer_from_float_constant(value: &Constant) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = constant(cx, cx.typeck_results(), receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -258,7 +258,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -298,7 +298,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -384,8 +384,8 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { _ ) = &add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1); - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1); + if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1); + if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1); if Int(2) == lvalue && Int(2) == rvalue; then { return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); @@ -416,7 +416,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); + if let Some(value) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind; if cx.typeck_results().expr_ty(self_arg).is_floating_point(); @@ -669,8 +669,8 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind; - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs); - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs); + if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs); + if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs); then { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index d8f4a5fe221bf..521045a9fed8f 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -89,11 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck { // Catching: // (fn_ptr as * ) == - _ if matches!( - constant(cx, cx.typeck_results(), to_check), - Some((Constant::RawPtr(0), _)) - ) => - { + _ if matches!(constant(cx, cx.typeck_results(), to_check), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); }, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 012aa5a1d1daf..ee7973b82ab97 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -101,10 +101,10 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let tr = cx.typeck_results(); - if let Some((Constant::Int(c), _)) = constant(cx, tr, r) { + if let Some(Constant::Int(c)) = constant(cx, tr, r) { return Some((c, op.node, l)); }; - if let Some((Constant::Int(c), _)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = constant(cx, tr, l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index bdeddf44df7bd..7a269e98ff13b 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { let parent_id = map.parent_id(expr.hir_id); if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind; - if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr); + if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); if let Ok(index_value) = index_value.try_into(); if index_value < max_suggested_slice; diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 924a361c0f6a8..22c14d9b04dd1 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -191,18 +191,14 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option, Option) { - let s = range - .start - .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); + let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range - .end - .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); + let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 16772a9d5987b..e661418092080 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; -use rustc_hir::{ExprKind, Local, PatKind}; +use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::IsSuggestable; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{BytePos, Span}; @@ -138,7 +140,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { if !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind && let Some(init) = local.init @@ -191,15 +193,17 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if local.pat.default_binding_modes && local.ty.is_none() { // When `default_binding_modes` is true, the `let` keyword is present. - // Ignore function calls that return impl traits... - if let Some(init) = local.init && - matches!(init.kind, ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _)) { - let expr_ty = cx.typeck_results().expr_ty(init); - if expr_ty.is_impl_trait() { - return; - } - } + // Ignore unnameable types + if let Some(init) = local.init + && !cx.typeck_results().expr_ty(init).is_suggestable(cx.tcx, true) + { + return; + } + // Ignore if it is from a procedural macro... + if is_from_proc_macro(cx, init) { + return; + } span_lint_and_help( cx, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3517842a01e7b..b442a4ac5f611 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -266,6 +266,7 @@ mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; mod ref_option_ref; +mod ref_patterns; mod reference; mod regex; mod return_self_not_must_use; @@ -331,8 +332,11 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` -use crate::utils::conf::{format_error, TryConf}; pub use crate::utils::conf::{lookup_conf_file, Conf}; +use crate::utils::{ + conf::{format_error, metadata::get_configuration_metadata, TryConf}, + FindAll, +}; /// Register all pre expansion lints /// @@ -471,7 +475,22 @@ pub(crate) struct LintInfo { pub fn explain(name: &str) { let target = format!("clippy::{}", name.to_ascii_uppercase()); match declared_lints::LINTS.iter().find(|info| info.lint.name == target) { - Some(info) => print!("{}", info.explanation), + Some(info) => { + println!("{}", info.explanation); + // Check if the lint has configuration + let mdconf = get_configuration_metadata(); + if let Some(config_vec_positions) = mdconf + .iter() + .find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned())) + { + // If it has, print it + println!("### Configuration for {}:\n", info.lint.name_lower()); + for position in config_vec_positions { + let conf = &mdconf[position]; + println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default); + } + } + }, None => println!("unknown lint: {name}"), } } @@ -971,6 +990,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments)); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); + store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index bba9bb445a771..09b2032e20fbe 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` - /// instance in all cases. There two cases where the suggestion might not be + /// instance in all cases. There are two cases where the suggestion might not be /// appropriate or necessary: /// /// - If the `Lines` instance can never produce any error, or if an error is produced diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 1247370b74a2f..3f8b42ffe8053 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -38,7 +38,6 @@ declare_clippy_lint! { /// Could be written: /// /// ```rust - /// # #![feature(let_else)] /// # fn main () { /// # let w = Some(0); /// let Some(v) = w else { return }; @@ -69,29 +68,23 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); impl<'tcx> LateLintPass<'tcx> for ManualLetElse { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { - let if_let_or_match = if_chain! { - if self.msrv.meets(msrvs::LET_ELSE); - if !in_external_macro(cx.sess(), stmt.span); - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if local.els.is_none(); - if local.ty.is_none(); - if init.span.ctxt() == stmt.span.ctxt(); - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init); - then { - if_let_or_match - } else { - return; - } - }; + if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { + return; + } + if let StmtKind::Local(local) = stmt.kind && + let Some(init) = local.init && + local.els.is_none() && + local.ty.is_none() && + init.span.ctxt() == stmt.span.ctxt() && + let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { match if_let_or_match { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { if expr_is_simple_identity(let_pat, if_then); if let Some(if_else) = if_else; if expr_diverges(cx, if_else); then { - emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else); + emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); } }, IfLetOrMatch::Match(match_expr, arms, source) => { @@ -128,15 +121,23 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { return; } - emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body); + emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); }, } + }; } extract_msrv_attr!(LateContext); } -fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) { +fn emit_manual_let_else( + cx: &LateContext<'_>, + span: Span, + expr: &Expr<'_>, + local: &Pat<'_>, + pat: &Pat<'_>, + else_body: &Expr<'_>, +) { span_lint_and_then( cx, MANUAL_LET_ELSE, @@ -145,12 +146,11 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: |diag| { // This is far from perfect, for example there needs to be: // * mut additions for the bindings - // * renamings of the bindings + // * renamings of the bindings for `PatKind::Or` // * unused binding collision detection with existing ones // * putting patterns with at the top level | inside () // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); @@ -159,10 +159,21 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: } else { format!("{{ {sn_else} }}") }; - let sn_bl = if matches!(pat.kind, PatKind::Or(..)) { - format!("({sn_pat})") - } else { - sn_pat.into_owned() + let sn_bl = match pat.kind { + PatKind::Or(..) => { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + format!("({sn_pat})") + }, + // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. + PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => { + let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); + let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app); + format!("{sn_wrapper}({sn_inner})") + }, + _ => { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + sn_pat.into_owned() + }, }; let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 7d28c11162458..93d977a5c96b8 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -144,7 +144,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E // Returns the length of the `expr` if it's a constant string or char. fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let (value, _) = constant(cx, cx.typeck_results(), expr)?; + let value = constant(cx, cx.typeck_results(), expr)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 33bc20dad6b78..0064619ef89d1 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,10 +1,12 @@ +use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; use clippy_utils::span_contains_comment; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; @@ -99,6 +101,14 @@ where } } + for arm in iter_without_last.clone() { + if let Some(pat) = arm.1 { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { + return false; + } + } + } + // The suggestion may be incorrect, because some arms can have `cfg` attributes // evaluated into `false` and so such arms will be stripped before. let mut applicability = Applicability::MaybeIncorrect; @@ -170,3 +180,13 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { _ => None, } } + +fn is_some(path_kind: PatKind<'_>) -> bool { + match path_kind { + PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { + let name = path.segments[0].ident; + name.name == rustc_span::sym::Some + }, + _ => false, + } +} diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index a48f4c77f857f..ae8262ace9687 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -282,9 +282,8 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. ExprKind::Lit(lit) => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes, _) => Self::LitBytes(bytes), + LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), - LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val), LitKind::Bool(val) => Self::LitBool(val), diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 87b63eead253e..55ec9d4474f59 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -25,9 +25,9 @@ mod wild_in_or_pats; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match}; +use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -1147,12 +1147,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { // Assume true. This would require either an invalid span, or one which crosses file boundaries. return true; }; - let mut pos = 0usize; - let mut iter = tokenize(&snip).map(|t| { - let start = pos; - pos += t.len as usize; - (t.kind, start..pos) - }); + let mut iter = tokenize_with_text(&snip); // Search for the token sequence [`#`, `[`, `cfg`] while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { @@ -1163,7 +1158,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { ) }); if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg") + && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) { return true; } diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index ae69ca8a3393d..abf2525a61c68 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -34,7 +34,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, + Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; let min_constant = mir::ConstantKind::from_value( @@ -45,7 +45,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }, }; let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0, + Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; let max_constant = mir::ConstantKind::from_value( diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 0809837d1fd76..e81e09da42547 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -189,73 +189,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - let found_good_method = match node_pair { - ( - PatKind::TupleStruct(ref path_left, patterns_left, _), - PatKind::TupleStruct(ref path_right, patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(ResultOk), - Item::Lang(ResultErr), - "is_ok()", - "is_err()", - ) - .or_else(|| { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Diag(sym::IpAddr, sym!(V4)), - Item::Diag(sym::IpAddr, sym!(V6)), - "is_ipv4()", - "is_ipv6()", - ) - }) - } else { - None - } - }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) - if patterns.len() == 1 => - { - if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(OptionSome), - Item::Lang(OptionNone), - "is_some()", - "is_none()", - ) - .or_else(|| { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(PollReady), - Item::Lang(PollPending), - "is_ready()", - "is_pending()", - ) - }) - } else { - None - } - }, - _ => None, - }; - - if let Some(good_method) = found_good_method { + if let Some(good_method) = found_good_method(cx, arms, node_pair) { let span = expr.span.to(op.span); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, @@ -279,6 +213,127 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } } +fn found_good_method<'a>( + cx: &LateContext<'_>, + arms: &[Arm<'_>], + node: (&PatKind<'_>, &PatKind<'_>), +) -> Option<&'a str> { + match node { + ( + PatKind::TupleStruct(ref path_left, patterns_left, _), + PatKind::TupleStruct(ref path_right, patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(ResultOk), + Item::Lang(ResultErr), + "is_ok()", + "is_err()", + ) + .or_else(|| { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Diag(sym::IpAddr, sym!(V4)), + Item::Diag(sym::IpAddr, sym!(V6)), + "is_ipv4()", + "is_ipv6()", + ) + }) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(OptionSome), + Item::Lang(OptionNone), + "is_some()", + "is_none()", + ) + .or_else(|| { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(PollReady), + Item::Lang(PollPending), + "is_ready()", + "is_pending()", + ) + }) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + get_good_method(cx, arms, path_left) + } else { + None + } + }, + (PatKind::Path(ref path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + _ => None, + } +} + +fn get_ident(path: &QPath<'_>) -> Option { + match path { + QPath::Resolved(_, path) => { + let name = path.segments[0].ident; + Some(name) + }, + _ => None, + } +} + +fn get_good_method<'a>(cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>) -> Option<&'a str> { + if let Some(name) = get_ident(path_left) { + return match name.as_str() { + "Ok" => { + find_good_method_for_matches_macro(cx, arms, path_left, Item::Lang(ResultOk), "is_ok()", "is_err()") + }, + "Err" => { + find_good_method_for_matches_macro(cx, arms, path_left, Item::Lang(ResultErr), "is_err()", "is_ok()") + }, + "Some" => find_good_method_for_matches_macro( + cx, + arms, + path_left, + Item::Lang(OptionSome), + "is_some()", + "is_none()", + ), + "None" => find_good_method_for_matches_macro( + cx, + arms, + path_left, + Item::Lang(OptionNone), + "is_none()", + "is_some()", + ), + _ => None, + }; + } + None +} + #[derive(Clone, Copy)] enum Item { Lang(LangItem), @@ -346,3 +401,29 @@ fn find_good_method_for_match<'a>( _ => None, } } + +fn find_good_method_for_matches_macro<'a>( + cx: &LateContext<'_>, + arms: &[Arm<'_>], + path_left: &QPath<'_>, + expected_item_left: Item, + should_be_left: &'a str, + should_be_right: &'a str, +) -> Option<&'a str> { + let first_pat = arms[0].pat; + + let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) { + (&arms[0].body.kind, &arms[1].body.kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + _ => None, + }, + _ => None, + } +} diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index c830958d5c80e..d1609eebfdca9 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -13,7 +13,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 64c09214a7683..b631cd00cda43 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) { + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/clippy_lints/src/methods/manual_next_back.rs b/clippy_lints/src/methods/manual_next_back.rs new file mode 100644 index 0000000000000..5f3fec53827a9 --- /dev/null +++ b/clippy_lints/src/methods/manual_next_back.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + rev_call: &'tcx Expr<'_>, + rev_recv: &'tcx Expr<'_>, +) { + let rev_recv_ty = cx.typeck_results().expr_ty(rev_recv); + + // check that the receiver of `rev` implements `DoubleEndedIterator` and + // that `rev` and `next` come from `Iterator` + if cx + .tcx + .get_diagnostic_item(sym::DoubleEndedIterator) + .map_or(false, |double_ended_iterator| { + implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]) + }) + && is_trait_method(cx, rev_call, sym::Iterator) + && is_trait_method(cx, expr, sym::Iterator) + { + span_lint_and_sugg( + cx, + super::MANUAL_NEXT_BACK, + expr.span.with_lo(rev_recv.span.hi()), + "manual backwards iteration", + "use", + String::from(".next_back()"), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 06b88e34d2462..9a594d964ab22 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -45,6 +45,7 @@ mod iter_overeager_cloned; mod iter_skip_next; mod iter_with_drain; mod iterator_step_by_zero; +mod manual_next_back; mod manual_ok_or; mod manual_saturating_arithmetic; mod manual_str_repeat; @@ -3132,8 +3133,11 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::>().len(); - /// // should be + /// let len = iterator.collect::>().len(); + /// ``` + /// Use instead: + /// ```rust + /// # let iterator = vec![1].into_iter(); /// let len = iterator.count(); /// ``` #[clippy::version = "1.30.0"] @@ -3193,6 +3197,29 @@ declare_clippy_lint! { "calling `drain` in order to `clear` a container" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.rev().next()` on a `DoubleEndedIterator` + /// + /// ### Why is this bad? + /// `.next_back()` is cleaner. + /// + /// ### Example + /// ```rust + /// # let foo = [0; 10]; + /// foo.iter().rev().next(); + /// ``` + /// Use instead: + /// ```rust + /// # let foo = [0; 10]; + /// foo.iter().next_back(); + /// ``` + #[clippy::version = "1.71.0"] + pub MANUAL_NEXT_BACK, + style, + "manual reverse iteration of `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3321,6 +3348,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_COLLECT, SUSPICIOUS_COMMAND_ARG_SPACE, CLEAR_WITH_DRAIN, + MANUAL_NEXT_BACK, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3677,6 +3705,7 @@ impl Methods { ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), + ("rev", [])=> manual_next_back::check(cx, expr, recv, recv2), _ => {}, } } @@ -3741,13 +3770,13 @@ impl Methods { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 0b0c6adc5045a..6841aaf626ca5 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -1,6 +1,5 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; @@ -8,6 +7,7 @@ use clippy_utils::{ can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, CaptureKind, }; +use clippy_utils::{fn_def_id, higher}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; @@ -16,7 +16,7 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_middle::ty::{self, AssocKind, Clause, EarlyBinder, GenericArg, GenericArgKind, PredicateKind, Ty}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol}; @@ -32,6 +32,8 @@ pub(super) fn check<'tcx>( if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { match parent { Node::Expr(parent) => { + check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { let mut app = Applicability::MachineApplicable; let name = name.ident.as_str(); @@ -134,6 +136,68 @@ pub(super) fn check<'tcx>( } } +/// checks for for collecting into a (generic) method or function argument +/// taking an `IntoIterator` +fn check_collect_into_intoiterator<'tcx>( + cx: &LateContext<'tcx>, + parent: &'tcx Expr<'tcx>, + collect_expr: &'tcx Expr<'tcx>, + call_span: Span, + iter_expr: &'tcx Expr<'tcx>, +) { + if let Some(id) = fn_def_id(cx, parent) { + let args = match parent.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => args, + _ => &[], + }; + // find the argument index of the `collect_expr` in the + // function / method call + if let Some(arg_idx) = args.iter().position(|e| e.hir_id == collect_expr.hir_id).map(|i| { + if matches!(parent.kind, ExprKind::MethodCall(_, _, _, _)) { + i + 1 + } else { + i + } + }) { + // extract the input types of the function/method call + // that contains `collect_expr` + let inputs = cx + .tcx + .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).subst_identity()) + .inputs(); + + // map IntoIterator generic bounds to their signature + // types and check whether the argument type is an + // `IntoIterator` + if cx + .tcx + .param_env(id) + .caller_bounds() + .into_iter() + .filter_map(|p| { + if let PredicateKind::Clause(Clause::Trait(t)) = p.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator,t.trait_ref.def_id) { + Some(t.self_ty()) + } else { + None + } + }) + .any(|ty| ty == inputs[arg_idx]) + { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_lo(iter_expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "remove this call", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index a345ec813ff50..bb4cdd2a6fa10 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) { + if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 91f7ce1dbe58e..5ea12c441840d 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -316,7 +316,7 @@ fn parse_iter_usage<'tcx>( }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { + if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { let span = if name.ident.as_str() == "nth" { e.span } else { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 3752b9a946f8e..303f012569087 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -16,9 +16,12 @@ use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, + get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, + last_path_segment, SpanlessEq, }; +use crate::ref_patterns::REF_PATTERNS; + declare_clippy_lint! { /// ### What it does /// Checks for function arguments and let bindings denoted as @@ -162,6 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { return; } for arg in iter_input_pats(decl, body) { + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { + return; + } if let PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..) = arg.pat.kind { span_lint( cx, @@ -180,6 +187,8 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; if let Some(init) = local.init; + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + if is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id); then { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 71281a0b40b0a..62af42a3961f8 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -146,7 +146,7 @@ fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if e.span.from_expansion() { + if e.span.from_expansion() || !span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() { return; } if let Some(higher::If { @@ -209,8 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } if let Some((lhs_a, a)) = fetch_assign(then) && let Some((lhs_b, b)) = fetch_assign(r#else) && - SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) && - span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() + SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index d29ca37eaeb80..f4863600ccc04 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?.0; + let cv = constant(cx, cx.typeck_results(), expr)?; let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index f72595987ee2f..5c240276b76d1 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -21,7 +21,7 @@ const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ ["f64", "f64"], ["std::num::Saturating", "std::num::Saturating"], ["std::num::Wrapping", "std::num::Wrapping"], - ["std::string::String", "&str"], + ["std::string::String", "str"], ]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; const INTEGER_METHODS: &[&str] = &["saturating_div", "wrapping_div", "wrapping_rem", "wrapping_rem_euclid"]; @@ -113,7 +113,7 @@ impl ArithmeticSideEffects { if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { return Some(n) } - if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) { return Some(n); } None @@ -144,8 +144,10 @@ impl ArithmeticSideEffects { ) { return; }; - let lhs_ty = cx.typeck_results().expr_ty(lhs); - let rhs_ty = cx.typeck_results().expr_ty(rhs); + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); + let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } @@ -154,8 +156,6 @@ impl ArithmeticSideEffects { // 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 ( Self::literal_integer(cx, actual_lhs), Self::literal_integer(cx, actual_rhs), diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 1369b3e74625c..1fddf0f50e322 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: } fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option { - match constant(cx, cx.typeck_results(), lit)?.0 { + match constant(cx, cx.typeck_results(), lit)? { Constant::Int(n) => Some(n), _ => None, } diff --git a/clippy_lints/src/operators/cmp_nan.rs b/clippy_lints/src/operators/cmp_nan.rs index 786ae1552ad3d..e18064b7061bf 100644 --- a/clippy_lints/src/operators/cmp_nan.rs +++ b/clippy_lints/src/operators/cmp_nan.rs @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp } fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - if let Some((value, _)) = constant(cx, cx.typeck_results(), e) { + if let Some(value) = constant(cx, cx.typeck_results(), e) { match value { Constant::F32(num) => num.is_nan(), Constant::F64(num) => num.is_nan(), diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 49e662cacb0c3..f120be13836d8 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right) + && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right) { let suggested_fn = match (method_path.ident.as_str(), divisor) { ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 97ddcdb24799d..15dff126be76e 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{constant_with_source, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; @@ -18,9 +18,16 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { - if is_allowed(cx, left) || is_allowed(cx, right) { - return; - } + let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { + Some((c, s)) if !is_allowed(&c) => s.is_local(), + Some(_) => return, + None => true, + }; + let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { + Some((c, s)) if !is_allowed(&c) => s.is_local(), + Some(_) => return, + None => true, + }; // Allow comparing the results of signum() if is_signum(cx, left) && is_signum(cx, right) { @@ -34,10 +41,7 @@ pub(crate) fn check<'tcx>( } } let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); - let (lint, msg) = get_lint_and_message( - is_named_constant(cx, left) || is_named_constant(cx, right), - is_comparing_arrays, - ); + let (lint, msg) = get_lint_and_message(left_is_local && right_is_local, is_comparing_arrays); span_lint_and_then(cx, lint, expr.span, msg, |diag| { let lhs = Sugg::hir(cx, left, ".."); let rhs = Sugg::hir(cx, right, ".."); @@ -59,44 +63,33 @@ pub(crate) fn check<'tcx>( } } -fn get_lint_and_message( - is_comparing_constants: bool, - is_comparing_arrays: bool, -) -> (&'static rustc_lint::Lint, &'static str) { - if is_comparing_constants { +fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static rustc_lint::Lint, &'static str) { + if is_local { ( - FLOAT_CMP_CONST, + FLOAT_CMP, if is_comparing_arrays { - "strict comparison of `f32` or `f64` constant arrays" + "strict comparison of `f32` or `f64` arrays" } else { - "strict comparison of `f32` or `f64` constant" + "strict comparison of `f32` or `f64`" }, ) } else { ( - FLOAT_CMP, + FLOAT_CMP_CONST, if is_comparing_arrays { - "strict comparison of `f32` or `f64` arrays" + "strict comparison of `f32` or `f64` constant arrays" } else { - "strict comparison of `f32` or `f64`" + "strict comparison of `f32` or `f64` constant" }, ) } } -fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) { - res - } else { - false - } -} - -fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), expr) { - Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(), - Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(), - Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f { +fn is_allowed(val: &Constant) -> bool { + match val { + &Constant::F32(f) => f == 0.0 || f.is_infinite(), + &Constant::F64(f) => f == 0.0 || f.is_infinite(), + Constant::Vec(vec) => vec.iter().all(|f| match f { Constant::F32(f) => *f == 0.0 || (*f).is_infinite(), Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 19599731bd6ef..d63a836e73d6f 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -96,32 +96,6 @@ declare_clippy_lint! { "any arithmetic expression that can cause side effects like overflows or panics" } -declare_clippy_lint! { - /// ### What it does - /// Checks for integer arithmetic operations which could overflow or panic. - /// - /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable - /// of overflowing according to the [Rust - /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), - /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is - /// attempted. - /// - /// ### Why is this bad? - /// Integer overflow will trigger a panic in debug builds or will wrap in - /// release mode. Division by zero will cause a panic in either mode. In some applications one - /// wants explicitly checked, wrapping or saturating arithmetic. - /// - /// ### Example - /// ```rust - /// # let a = 0; - /// a + 1; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INTEGER_ARITHMETIC, - restriction, - "any integer arithmetic expression which could overflow or panic" -} - declare_clippy_lint! { /// ### What it does /// Checks for float arithmetic. @@ -787,7 +761,6 @@ pub struct Operators { impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, ARITHMETIC_SIDE_EFFECTS, - INTEGER_ARITHMETIC, FLOAT_ARITHMETIC, ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP, diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index af4e74947f41d..a2c3a4d8ba775 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -40,7 +40,7 @@ struct OperandInfo { fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { match constant(cx, cx.typeck_results(), operand) { - Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() { + Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); return Some(OperandInfo { @@ -58,10 +58,10 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> }, _ => {}, }, - Some((Constant::F32(f), _)) => { + Some(Constant::F32(f)) => { return Some(floating_point_operand_info(&f)); }, - Some((Constant::F64(f), _)) => { + Some(Constant::F64(f)) => { return Some(floating_point_operand_info(&f)); }, _ => {}, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 77fd45b199a11..102845ceed095 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,8 +1,6 @@ -use super::{FLOAT_ARITHMETIC, INTEGER_ARITHMETIC}; +use super::FLOAT_ARITHMETIC; use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_from_proc_macro; -use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -45,31 +43,8 @@ impl Context { _ => (), } - let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); - if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { - if is_from_proc_macro(cx, expr) { - return; - } - match op { - hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { - hir::ExprKind::Lit(_lit) => (), - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if is_integer_literal(expr, 1) { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } - }, - _ => { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - }, - }, - _ => { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - }, - } - } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + let (_, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); + if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } @@ -80,17 +55,9 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - if ty.is_integral() { - if is_from_proc_macro(cx, expr) { - return; - } - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } else if ty.is_floating_point() { - span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { + span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); + self.expr_id = Some(expr.hir_id); } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index bbbcda069c551..aa6d40042688d 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -122,7 +122,7 @@ fn try_get_option_occurrence<'tcx>( ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, _ => expr, }; - let inner_pat = try_get_inner_pat(cx, pat)?; + let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; if_chain! { if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind; if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); @@ -176,7 +176,7 @@ fn try_get_option_occurrence<'tcx>( ), none_expr: format!( "{}{}", - if method_sugg == "map_or" { "" } else { "|| " }, + if method_sugg == "map_or" { "" } else if is_result { "|_| " } else { "|| "}, Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), ), }); @@ -186,11 +186,13 @@ fn try_get_option_occurrence<'tcx>( None } -fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { +fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) { - return Some(inner_pat); + if is_res_lang_ctor(cx, res, OptionSome) { + return Some((inner_pat, false)); + } else if is_res_lang_ctor(cx, res, ResultOk) { + return Some((inner_pat, true)); } } None diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index fc655fe2d0bb3..dd7ded491e792 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -319,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option return None, }; if let Some(id) = path_to_local(l) { - if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { + if let Some(c) = constant(cx, cx.typeck_results(), r) { return Some(RangeBounds { val: c, expr: r, @@ -331,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option, expr: &Expr<'_>) { if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr); let ty = cx.typeck_results().expr_ty(start); if let ty::Int(_) | ty::Uint(_) = ty.kind(); - if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); - if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end); + if let Some(start_idx) = constant(cx, cx.typeck_results(), start); + if let Some(end_idx) = constant(cx, cx.typeck_results(), end); if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { diff --git a/clippy_lints/src/ref_patterns.rs b/clippy_lints/src/ref_patterns.rs new file mode 100644 index 0000000000000..b1530eed1c11f --- /dev/null +++ b/clippy_lints/src/ref_patterns.rs @@ -0,0 +1,44 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{BindingAnnotation, Pat, PatKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of the `ref` keyword. + /// ### Why is this bad? + /// The `ref` keyword can be confusing for people unfamiliar with it, and often + /// it is more concise to use `&` instead. + /// ### Example + /// ```rust + /// let opt = Some(5); + /// if let Some(ref foo) = opt {} + /// ``` + /// Use instead: + /// ```rust + /// let opt = Some(5); + /// if let Some(foo) = &opt {} + /// ``` + #[clippy::version = "1.71.0"] + pub REF_PATTERNS, + restriction, + "use of a ref pattern, e.g. Some(ref value)" +} +declare_lint_pass!(RefPatterns => [REF_PATTERNS]); + +impl EarlyLintPass for RefPatterns { + fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Ident(BindingAnnotation::REF, _, _) = pat.kind + && !pat.span.from_expansion() + { + span_lint_and_help( + cx, + REF_PATTERNS, + pat.span, + "usage of ref pattern", + None, + "consider using `&` for clarity instead", + ); + } + } +} diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index b8b32df6cc64b..ef19c6f461729 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -122,37 +122,39 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { - constant(cx, cx.typeck_results(), e).and_then(|(c, _)| match c { + constant(cx, cx.typeck_results(), e).and_then(|c| match c { Constant::Str(s) => Some(s), _ => None, }) } fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { - use regex_syntax::hir::Anchor::{EndText, StartText}; - use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; + use regex_syntax::hir::HirKind::{Alternation, Concat, Empty, Literal, Look}; + use regex_syntax::hir::Look as HirLook; let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_))); match *s.kind() { - Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), + Empty | Look(_) => Some("the regex is unlikely to be useful as it is"), Literal(_) => Some("consider using `str::contains`"), Alternation(ref exprs) => { - if exprs.iter().all(|e| e.kind().is_empty()) { + if exprs.iter().all(|e| matches!(e.kind(), Empty)) { Some("the regex is unlikely to be useful as it is") } else { None } }, Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) { - (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => { + (&Look(HirLook::Start), &Look(HirLook::End)) if exprs[1..(exprs.len() - 1)].is_empty() => { Some("consider using `str::is_empty`") }, - (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + (&Look(HirLook::Start), &Look(HirLook::End)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { Some("consider using `==` on `str`s") }, - (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"), - (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + (&Look(HirLook::Start), &Literal(_)) if is_literal(&exprs[1..]) => { + Some("consider using `str::starts_with`") + }, + (&Literal(_), &Look(HirLook::End)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { Some("consider using `str::ends_with`") }, _ if is_literal(exprs) => Some("consider using `str::contains`"), @@ -175,10 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { } fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - let mut parser = regex_syntax::ParserBuilder::new() - .unicode(true) - .allow_invalid_utf8(!utf8) - .build(); + let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build(); if let ExprKind::Lit(lit) = expr.kind { if let LitKind::Str(ref r, style) = lit.node { diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 52e22c0c6303c..a2c3465cde4a3 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -15,6 +15,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), ("clippy::new_without_default_derive", "clippy::new_without_default"), ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 5b588e914fdf8..483f860a8b5e2 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -132,7 +132,7 @@ declare_clippy_lint! { /// Probably lots of false positives. If an index comes from a known valid position (e.g. /// obtained via `char_indices` over the same string), it is totally OK. /// - /// # Example + /// ### Example /// ```rust,should_panic /// &"Ölkanne"[1..]; /// ``` diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index b5f11b4acae02..4ccda15068bbb 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -37,12 +37,12 @@ declare_clippy_lint! { #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, nursery, - "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" + "types are repeated unnecessarily in trait bounds, use `+` instead of using `T: _, T: _`" } declare_clippy_lint! { /// ### What it does - /// Checks for cases where generics are being used and multiple + /// Checks for cases where generics or trait objects are being used and multiple /// syntax specifications for trait bounds are used simultaneously. /// /// ### Why is this bad? @@ -167,6 +167,61 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + if_chain! { + if let TyKind::Ref(.., mut_ty) = &ty.kind; + if let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind; + if bounds.len() > 2; + then { + + // Build up a hash of every trait we've seen + // When we see a trait for the first time, add it to unique_traits + // so we can later use it to build a string of all traits exactly once, without duplicates + + let mut seen_def_ids = FxHashSet::default(); + let mut unique_traits = Vec::new(); + + // Iterate the bounds and add them to our seen hash + // If we haven't yet seen it, add it to the fixed traits + for bound in bounds.iter() { + let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; + + let new_trait = seen_def_ids.insert(def_id); + + if new_trait { + unique_traits.push(bound); + } + } + + // If the number of unique traits isn't the same as the number of traits in the bounds, + // there must be 1 or more duplicates + if bounds.len() != unique_traits.len() { + let mut bounds_span = bounds[0].span; + + for bound in bounds.iter().skip(1) { + bounds_span = bounds_span.to(bound.span); + } + + let fixed_trait_snippet = unique_traits + .iter() + .filter_map(|b| snippet_opt(cx, b.span)) + .collect::>() + .join(" + "); + + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + bounds_span, + "this trait bound is already specified in trait declaration", + "try", + fixed_trait_snippet, + Applicability::MaybeIncorrect, + ); + } + } + } + } } impl TraitBounds { diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index e75d7f6bf1d52..4944381da24d5 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -31,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t match arg.kind { // Catching: // transmute over constants that resolve to `null`. - ExprKind::Path(ref _qpath) - if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) => - { + ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); true }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 1e407fc4138c1..770914e99e168 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -16,9 +16,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t } // Catching transmute over constants that resolve to `null`. - let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) + let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index ddbe6b2c7904d..28c3fc859e332 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::is_ty_alias; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; @@ -138,6 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + if !is_ty_alias(qpath); then { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 935ea90d2452a..3c2bf5abab2b5 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -308,7 +308,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, vec); kind!("CStr(ref {vec})"); chain!(self, "let [{:?}] = **{vec}", vec.value); - } + }, LitKind::Str(s, _) => { bind!(self, s); kind!("Str({s}, _)"); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 5f05d971fce23..f6de66bb5145b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -174,16 +174,15 @@ macro_rules! define_Conf { } } - #[cfg(feature = "internal")] pub mod metadata { - use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; + use crate::utils::ClippyConfiguration; macro_rules! wrap_option { () => (None); ($x:literal) => (Some($x)); } - pub(crate) fn get_configuration_metadata() -> Vec { + pub fn get_configuration_metadata() -> Vec { vec![ $( { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 3d0d4a52511a8..7a1cd3effaef2 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,11 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::{ + collect_configs, + internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}, + ClippyConfiguration, +}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -520,111 +524,6 @@ impl Serialize for ApplicabilityInfo { } } -// ================================================================== -// Configuration -// ================================================================== -#[derive(Debug, Clone, Default)] -pub struct ClippyConfiguration { - name: String, - config_type: &'static str, - default: String, - lints: Vec, - doc: String, - #[allow(dead_code)] - deprecation_reason: Option<&'static str>, -} - -impl ClippyConfiguration { - pub fn new( - name: &'static str, - config_type: &'static str, - default: String, - doc_comment: &'static str, - deprecation_reason: Option<&'static str>, - ) -> Self { - let (lints, doc) = parse_config_field_doc(doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - Self { - name: to_kebab(name), - lints, - doc, - config_type, - default, - deprecation_reason, - } - } - - fn to_markdown_paragraph(&self) -> String { - format!( - "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", - self.name, - self.doc - .lines() - .map(|line| line.strip_prefix(" ").unwrap_or(line)) - .join("\n"), - self.default, - self.config_type, - self.lints - .iter() - .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .join("\n"), - ) - } - - fn to_markdown_table_entry(&self) -> String { - format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) - } -} - -fn collect_configs() -> Vec { - crate::utils::conf::metadata::get_configuration_metadata() -} - -/// This parses the field documentation of the config struct. -/// -/// ```rust, ignore -/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") -/// ``` -/// -/// Would yield: -/// ```rust, ignore -/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") -/// ``` -fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { - const DOC_START: &str = " Lint: "; - if_chain! { - if doc_comment.starts_with(DOC_START); - if let Some(split_pos) = doc_comment.find('.'); - then { - let mut doc_comment = doc_comment.to_string(); - let mut documentation = doc_comment.split_off(split_pos); - - // Extract lints - doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment - .split_off(DOC_START.len()) - .split(", ") - .map(str::to_string) - .collect(); - - // Format documentation correctly - // split off leading `.` from lint name list and indent for correct formatting - documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); - - Some((lints, documentation)) - } else { - None - } - } -} - -/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` -fn to_kebab(config_name: &str) -> String { - config_name.replace('_', "-") -} - impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index dc647af264c15..d3ea7cafa80c2 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -4,3 +4,143 @@ pub mod dump_hir; pub mod format_args_collector; #[cfg(feature = "internal")] pub mod internal_lints; +#[cfg(feature = "internal")] +use itertools::Itertools; + +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` +fn to_kebab(config_name: &str) -> String { + config_name.replace('_', "-") +} + +// ================================================================== +// Configuration +// ================================================================== +#[derive(Debug, Clone, Default)] //~ ERROR no such field +pub struct ClippyConfiguration { + pub name: String, + #[allow(dead_code)] + config_type: &'static str, + pub default: String, + pub lints: Vec, + pub doc: String, + #[allow(dead_code)] + deprecation_reason: Option<&'static str>, +} + +impl ClippyConfiguration { + pub fn new( + name: &'static str, + config_type: &'static str, + default: String, + doc_comment: &'static str, + deprecation_reason: Option<&'static str>, + ) -> Self { + let (lints, doc) = parse_config_field_doc(doc_comment) + .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); + + Self { + name: to_kebab(name), + lints, + doc, + config_type, + default, + deprecation_reason, + } + } + + #[cfg(feature = "internal")] + fn to_markdown_paragraph(&self) -> String { + format!( + "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", + self.name, + self.doc + .lines() + .map(|line| line.strip_prefix(" ").unwrap_or(line)) + .join("\n"), + self.default, + self.config_type, + self.lints + .iter() + .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) + .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) + .join("\n"), + ) + } + + #[cfg(feature = "internal")] + fn to_markdown_table_entry(&self) -> String { + format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) + } +} + +#[cfg(feature = "internal")] +fn collect_configs() -> Vec { + crate::utils::conf::metadata::get_configuration_metadata() +} + +/// This parses the field documentation of the config struct. +/// +/// ```rust, ignore +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") +/// ``` +/// +/// Would yield: +/// ```rust, ignore +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") +/// ``` +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { + const DOC_START: &str = " Lint: "; + if_chain! { + if doc_comment.starts_with(DOC_START); + if let Some(split_pos) = doc_comment.find('.'); + then { + let mut doc_comment = doc_comment.to_string(); + let mut documentation = doc_comment.split_off(split_pos); + + // Extract lints + doc_comment.make_ascii_lowercase(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); + + // Format documentation correctly + // split off leading `.` from lint name list and indent for correct formatting + documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); + + Some((lints, documentation)) + } else { + None + } + } +} + +// Shamelessly stolen from find_all (https://github.com/nectariner/find_all) +pub trait FindAll: Iterator + Sized { + fn find_all

(&mut self, predicate: P) -> Option> + where + P: FnMut(&Self::Item) -> bool; +} + +impl FindAll for I +where + I: Iterator, +{ + fn find_all

(&mut self, mut predicate: P) -> Option> + where + P: FnMut(&Self::Item) -> bool, + { + let mut occurences = Vec::::default(); + for (index, element) in self.enumerate() { + if predicate(&element) { + occurences.push(index); + } + } + + match occurences.len() { + 0 => None, + _ => Some(occurences), + } + } +} diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 297a80e5767a1..7329e508106d9 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -84,7 +84,7 @@ impl UselessVec { let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { - if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 36f910c983f64..a9089fba3c539 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -7,7 +7,7 @@ use rustc_hir::{ def::{DefKind, Res}, Item, ItemKind, PathSegment, UseKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; @@ -117,6 +117,10 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if cx.sess().is_test_crate() { + return; + } + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 8075881e3bb9d..fb772644c0d64 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -1,18 +1,21 @@ #![allow(clippy::float_cmp)] +use crate::source::{get_source_text, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{List, SubstsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{self, Equal}; use std::hash::{Hash, Hasher}; use std::iter; @@ -210,8 +213,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) => Constant::Binary(Lrc::clone(s)), - LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -228,27 +230,46 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { } } +/// The source of a constant value. +pub enum ConstantSource { + /// The value is determined solely from the expression. + Local, + /// The value is dependent on a defined constant. + Constant, +} +impl ConstantSource { + pub fn is_local(&self) -> bool { + matches!(self, Self::Local) + } +} + +/// Attempts to evaluate the expression as a constant. pub fn constant<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, -) -> Option<(Constant, bool)> { - let mut cx = ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: ty::List::empty(), - }; - cx.expr(e).map(|cst| (cst, cx.needed_resolution)) +) -> Option { + ConstEvalLateContext::new(lcx, typeck_results).expr(e) +} + +/// Attempts to evaluate the expression as a constant. +pub fn constant_with_source<'tcx>( + lcx: &LateContext<'tcx>, + typeck_results: &ty::TypeckResults<'tcx>, + e: &Expr<'_>, +) -> Option<(Constant, ConstantSource)> { + let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results); + let res = ctxt.expr(e); + res.map(|x| (x, ctxt.source)) } +/// Attempts to evaluate an expression only if it's value is not dependent on other items. pub fn constant_simple<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, ) -> Option { - constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) + constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) } pub fn constant_full_int<'tcx>( @@ -297,29 +318,25 @@ impl Ord for FullInt { } } -/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. -pub fn constant_context<'a, 'tcx>( - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, -) -> ConstEvalLateContext<'a, 'tcx> { - ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: ty::List::empty(), - } -} - pub struct ConstEvalLateContext<'a, 'tcx> { lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, - needed_resolution: bool, + source: ConstantSource, substs: SubstsRef<'tcx>, } impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { + fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { + Self { + lcx, + typeck_results, + param_env: lcx.param_env, + source: ConstantSource::Local, + substs: List::empty(), + } + } + /// Simple constant folding: Insert an expression, get a constant or none. pub fn expr(&mut self, e: &Expr<'_>) -> Option { match e.kind { @@ -454,11 +471,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None) .ok() .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?; - let result = miri_to_const(self.lcx.tcx, result); - if result.is_some() { - self.needed_resolution = true; - } - result + let result = miri_to_const(self.lcx.tcx, result)?; + self.source = ConstantSource::Constant; + Some(result) }, // FIXME: cover all usable cases. _ => None, @@ -492,8 +507,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// A block can only yield a constant if it only has one constant expression. fn block(&mut self, block: &Block<'_>) -> Option { - if block.stmts.is_empty() { - block.expr.as_ref().and_then(|b| self.expr(b)) + if block.stmts.is_empty() + && let Some(expr) = block.expr + { + // Try to detect any `cfg`ed statements or empty macro expansions. + let span = block.span.data(); + if span.ctxt == SyntaxContext::root() { + if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) + && let expr_lo = expr_span.lo() + && expr_lo >= span.lo + && let Some(src) = get_source_text(self.lcx, span.lo..expr_lo) + && let Some(src) = src.as_str() + { + use rustc_lexer::TokenKind::{Whitespace, LineComment, BlockComment, Semi, OpenBrace}; + if !tokenize(src) + .map(|t| t.kind) + .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) + .eq([OpenBrace]) + { + self.source = ConstantSource::Constant; + } + } else { + // Unable to access the source. Assume a non-local dependency. + self.source = ConstantSource::Constant; + } + } + + self.expr(expr) } else { None } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 9b7408d513392..a49246a783272 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,6 +1,7 @@ use crate::consts::constant_simple; use crate::macros::macro_backtrace; -use crate::source::snippet_opt; +use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange}; +use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; @@ -13,8 +14,9 @@ use rustc_hir::{ use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; use rustc_middle::ty::TypeckResults; -use rustc_span::{sym, Symbol}; +use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext}; use std::hash::{Hash, Hasher}; +use std::ops::Range; /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but /// other conditions would make them equal. @@ -65,6 +67,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, + left_ctxt: SyntaxContext::root(), + right_ctxt: SyntaxContext::root(), locals: HirIdMap::default(), } } @@ -92,6 +96,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, + left_ctxt: SyntaxContext, + right_ctxt: SyntaxContext, // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, @@ -126,52 +132,88 @@ impl HirEqInterExpr<'_, '_, '_> { } /// Checks whether two blocks are the same. + #[expect(clippy::similar_names)] fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - match (left.stmts, left.expr, right.stmts, right.expr) { - ([], None, [], None) => { - // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro - // expanded to nothing, or the cfg attribute was used. - let (Some(left), Some(right)) = ( - snippet_opt(self.inner.cx, left.span), - snippet_opt(self.inner.cx, right.span), - ) else { return true }; - let mut left_pos = 0; - let left = tokenize(&left) - .map(|t| { - let end = left_pos + t.len as usize; - let s = &left[left_pos..end]; - left_pos = end; - (t, s) - }) - .filter(|(t, _)| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }) - .map(|(_, s)| s); - let mut right_pos = 0; - let right = tokenize(&right) - .map(|t| { - let end = right_pos + t.len as usize; - let s = &right[right_pos..end]; - right_pos = end; - (t, s) - }) - .filter(|(t, _)| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }) - .map(|(_, s)| s); - left.eq(right) - }, - _ => { - over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r)) - && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) - }, + use TokenKind::{BlockComment, LineComment, Semi, Whitespace}; + if left.stmts.len() != right.stmts.len() { + return false; + } + let lspan = left.span.data(); + let rspan = right.span.data(); + if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() { + // Don't try to check in between statements inside macros. + return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right)) + && both(&left.expr, &right.expr, |left, right| self.eq_expr(left, right)); + } + if lspan.ctxt != rspan.ctxt { + return false; } + + let mut lstart = lspan.lo; + let mut rstart = rspan.lo; + + for (left, right) in left.stmts.iter().zip(right.stmts) { + if !self.eq_stmt(left, right) { + return false; + } + + // Try to detect any `cfg`ed statements or empty macro expansions. + let Some(lstmt_span) = walk_span_to_context(left.span, lspan.ctxt) else { + return false; + }; + let Some(rstmt_span) = walk_span_to_context(right.span, rspan.ctxt) else { + return false; + }; + let lstmt_span = lstmt_span.data(); + let rstmt_span = rstmt_span.data(); + + if lstmt_span.lo < lstart && rstmt_span.lo < rstart { + // Can happen when macros expand to multiple statements, or rearrange statements. + // Nothing in between the statements to check in this case. + continue; + } + if lstmt_span.lo < lstart || rstmt_span.lo < rstart { + // Only one of the blocks had a weird macro. + return false; + } + if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + }) { + return false; + } + + lstart = lstmt_span.hi; + rstart = rstmt_span.hi; + } + + let (lend, rend) = match (left.expr, right.expr) { + (Some(left), Some(right)) => { + if !self.eq_expr(left, right) { + return false; + } + let Some(lexpr_span) = walk_span_to_context(left.span, lspan.ctxt) else { + return false; + }; + let Some(rexpr_span) = walk_span_to_context(right.span, rspan.ctxt) else { + return false; + }; + (lexpr_span.lo(), rexpr_span.lo()) + }, + (None, None) => (lspan.hi, rspan.hi), + (Some(_), None) | (None, Some(_)) => return false, + }; + + if lend < lstart && rend < rstart { + // Can happen when macros rearrange the input. + // Nothing in between the statements to check in this case. + return true; + } else if lend < lstart || rend < rstart { + // Only one of the blocks had a weird macro + return false; + } + eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + }) } fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { @@ -207,7 +249,7 @@ impl HirEqInterExpr<'_, '_, '_> { #[expect(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() { + if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { return false; } @@ -440,6 +482,45 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty()) } + + fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool { + if self.left_ctxt == left && self.right_ctxt == right { + return true; + } else if self.left_ctxt == left || self.right_ctxt == right { + // Only one context has changed. This can only happen if the two nodes are written differently. + return false; + } else if left != SyntaxContext::root() { + let mut left_data = left.outer_expn_data(); + let mut right_data = right.outer_expn_data(); + loop { + use TokenKind::{BlockComment, LineComment, Whitespace}; + if left_data.macro_def_id != right_data.macro_def_id + || (matches!(left_data.kind, ExpnKind::Macro(MacroKind::Bang, name) if name == sym::cfg) + && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) + })) + { + // Either a different chain of macro calls, or different arguments to the `cfg` macro. + return false; + } + let left_ctxt = left_data.call_site.ctxt(); + let right_ctxt = right_data.call_site.ctxt(); + if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() { + break; + } + if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() { + // Different lengths for the expansion stack. This can only happen if nodes are written differently, + // or shouldn't be compared to start with. + return false; + } + left_data = left_ctxt.outer_expn_data(); + right_data = right_ctxt.outer_expn_data(); + } + } + self.left_ctxt = left; + self.right_ctxt = right; + true + } } /// Some simple reductions like `{ return }` => `return` @@ -1038,3 +1119,34 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { h.hash_expr(e); h.finish() } + +#[expect(clippy::similar_names)] +fn eq_span_tokens( + cx: &LateContext<'_>, + left: impl SpanRange, + right: impl SpanRange, + pred: impl Fn(TokenKind) -> bool, +) -> bool { + fn f(cx: &LateContext<'_>, left: Range, right: Range, pred: impl Fn(TokenKind) -> bool) -> bool { + if let Some(lsrc) = get_source_text(cx, left) + && let Some(lsrc) = lsrc.as_str() + && let Some(rsrc) = get_source_text(cx, right) + && let Some(rsrc) = rsrc.as_str() + { + let pred = |t: &(_, _)| pred(t.0); + let map = |(_, x)| x; + + let ltok = tokenize_with_text(lsrc) + .filter(pred) + .map(map); + let rtok = tokenize_with_text(rsrc) + .filter(pred) + .map(map); + ltok.eq(rtok) + } else { + // Unable to access the source. Conservatively assume the blocks aren't equal. + false + } + } + f(cx, left.into_range(), right.into_range(), pred) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 964104fc31d0e..575c29a6b6f97 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,5 +1,6 @@ #![feature(array_chunks)] #![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(lint_reasons)] #![feature(never_type)] @@ -76,6 +77,7 @@ use std::sync::OnceLock; use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; +use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; @@ -282,6 +284,15 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +/// Checks if the given `QPath` belongs to a type alias. +pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { + match *qpath { + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias, ..)), + QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, + _ => false, + } +} + /// Checks if the method call given in `expr` belongs to the given trait. /// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { @@ -1488,7 +1499,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty) && let Some(min_const) = miri_to_const(cx.tcx, min_const_kind) - && let Some((start_const, _)) = constant(cx, cx.typeck_results(), start) + && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const } else { @@ -1504,7 +1515,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty) && let Some(max_const) = miri_to_const(cx.tcx, max_const_kind) - && let Some((end_const, _)) = constant(cx, cx.typeck_results(), end) + && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const } else { @@ -1536,7 +1547,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool return true; } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); - if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { + if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { return value == v; } false @@ -2480,6 +2491,17 @@ pub fn walk_to_expr_usage<'tcx, T>( None } +/// Tokenizes the input while keeping the text associated with each token. +pub fn tokenize_with_text(s: &str) -> impl Iterator { + let mut pos = 0; + tokenize(s).map(move |t| { + let end = pos + t.len; + let range = pos as usize..end as usize; + pos = end; + (t.kind, s.get(range).unwrap_or_default()) + }) +} + /// Checks whether a given span has any comment token /// This checks for all types of comment: line "//", block "/**", doc "///" "//!" pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { @@ -2496,23 +2518,11 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { /// Comments are returned wrapped with their relevant delimiters pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { let snippet = sm.span_to_snippet(span).unwrap_or_default(); - let mut comments_buf: Vec = Vec::new(); - let mut index: usize = 0; - - for token in tokenize(&snippet) { - let token_range = index..(index + token.len as usize); - index += token.len as usize; - match token.kind { - TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => { - if let Some(comment) = snippet.get(token_range) { - comments_buf.push(comment.to_string()); - } - }, - _ => (), - } - } - - comments_buf.join("\n") + let res = tokenize_with_text(&snippet) + .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) + .map(|(_, s)| s) + .join("\n"); + res } pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 62fa37660fad5..0f60290644a18 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -2,14 +2,64 @@ #![allow(clippy::module_name_repetitions)] +use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_session::Session; -use rustc_span::hygiene; use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{hygiene, SourceFile}; use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; +use std::ops::Range; + +/// A type which can be converted to the range portion of a `Span`. +pub trait SpanRange { + fn into_range(self) -> Range; +} +impl SpanRange for Span { + fn into_range(self) -> Range { + let data = self.data(); + data.lo..data.hi + } +} +impl SpanRange for SpanData { + fn into_range(self) -> Range { + self.lo..self.hi + } +} +impl SpanRange for Range { + fn into_range(self) -> Range { + self + } +} + +pub struct SourceFileRange { + pub sf: Lrc, + pub range: Range, +} +impl SourceFileRange { + /// Attempts to get the text from the source file. This can fail if the source text isn't + /// loaded. + pub fn as_str(&self) -> Option<&str> { + self.sf.src.as_ref().and_then(|x| x.get(self.range.clone())) + } +} + +/// Gets the source file, and range in the file, of the given span. Returns `None` if the span +/// extends through multiple files, or is malformed. +pub fn get_source_text(cx: &impl LintContext, sp: impl SpanRange) -> Option { + fn f(sm: &SourceMap, sp: Range) -> Option { + let start = sm.lookup_byte_offset(sp.start); + let end = sm.lookup_byte_offset(sp.end); + if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { + return None; + } + let range = start.pos.to_usize()..end.pos.to_usize(); + Some(SourceFileRange { sf: start.sf, range }) + } + f(cx.sess().source_map(), sp.into_range()) +} /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. pub fn expr_block( diff --git a/rust-toolchain b/rust-toolchain index 60b8a5ac07193..bc7fb711ed8b8 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-05-05" +channel = "nightly-2023-05-20" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index acb98d7ba98e6..99ce702839094 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -3,6 +3,7 @@ //@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs" //@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" //@normalize-stderr-test: "'rustc'" -> "''" +//@normalize-stderr-test: "running on .*" -> "running on " //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" #![deny(clippy::internal)] diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index b4619e980f303..0fc385cd6935a 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,12 +1,14 @@ thread '' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. +error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new +note: rustc 1.71.0-nightly (521f4dae1 2023-05-19) running on + +note: compiler flags: -C prefer-dynamic -Z ui-testing + note: Clippy version: foo thread panicked while panicking. aborting. diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index ab408bdf261ef..f95af1017bcbb 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -458,4 +458,12 @@ pub fn issue_10583(a: u16) -> u16 { 10 / a } +pub fn issue_10767() { + let n = &1.0; + n + n; + 3.1_f32 + &1.2_f32; + &3.4_f32 + 1.5_f32; + &3.5_f32 + &1.3_f32; +} + fn main() {} diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed index 165e4bc827239..755264617920e 100644 --- a/tests/ui/borrow_deref_ref.fixed +++ b/tests/ui/borrow_deref_ref.fixed @@ -1,7 +1,11 @@ //@run-rustfix +//@aux-build: proc_macros.rs #![allow(dead_code, unused_variables)] +extern crate proc_macros; +use proc_macros::with_span; + fn main() {} mod should_lint { @@ -47,6 +51,17 @@ mod should_not_lint2 { } } +with_span!( + span + + fn just_returning(x: &u32) -> &u32 { + x + } + + fn dont_lint_proc_macro() { + let a = &mut &*just_returning(&12); + } +); // this mod explains why we should not lint `& &* (&T)` mod false_negative { fn foo() { diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index 66c8d69bef983..e319d365f7e77 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -1,7 +1,11 @@ //@run-rustfix +//@aux-build: proc_macros.rs #![allow(dead_code, unused_variables)] +extern crate proc_macros; +use proc_macros::with_span; + fn main() {} mod should_lint { @@ -47,6 +51,17 @@ mod should_not_lint2 { } } +with_span!( + span + + fn just_returning(x: &u32) -> &u32 { + x + } + + fn dont_lint_proc_macro() { + let a = &mut &*just_returning(&12); + } +); // this mod explains why we should not lint `& &* (&T)` mod false_negative { fn foo() { diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr index d72de37c69ff5..1e47cda679601 100644 --- a/tests/ui/borrow_deref_ref.stderr +++ b/tests/ui/borrow_deref_ref.stderr @@ -1,5 +1,5 @@ error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:10:17 + --> $DIR/borrow_deref_ref.rs:14:17 | LL | let b = &*a; | ^^^ help: if you would like to reborrow, try removing `&*`: `a` @@ -7,13 +7,13 @@ LL | let b = &*a; = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:12:22 + --> $DIR/borrow_deref_ref.rs:16:22 | LL | let b = &mut &*bar(&12); | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:55:23 + --> $DIR/borrow_deref_ref.rs:70:23 | LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index e6331290420b4..840902b5323e7 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -35,6 +35,13 @@ fn main() { let _more = ret_ty_fn(); call_ty_fn(Box::default()); issue_10381(); + + // `Box::>::default()` would be valid here, but not `Box::default()` or + // `Box::::default()` + // + // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 + let mut unnameable = Box::new(Option::default()); + let _ = unnameable.insert(|| {}); } fn ret_ty_fn() -> Box { diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 34a05a29c5aa3..3618486a4732e 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -35,6 +35,13 @@ fn main() { let _more = ret_ty_fn(); call_ty_fn(Box::new(u8::default())); issue_10381(); + + // `Box::>::default()` would be valid here, but not `Box::default()` or + // `Box::::default()` + // + // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 + let mut unnameable = Box::new(Option::default()); + let _ = unnameable.insert(|| {}); } fn ret_ty_fn() -> Box { diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index c983486360144..13dfc5ae48a22 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -73,25 +73,25 @@ LL | call_ty_fn(Box::new(u8::default())); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:41:5 + --> $DIR/box_default.rs:48:5 | LL | Box::new(bool::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:58:28 + --> $DIR/box_default.rs:65:28 | LL | let _: Box = Box::new(ImplementsDefault::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:67:17 + --> $DIR/box_default.rs:74:17 | LL | let _ = Box::new(WeirdPathed::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:79:18 + --> $DIR/box_default.rs:86:18 | LL | Some(Box::new(Foo::default())) | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index d2aba2ac59b8a..c6514a559340c 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -1,5 +1,10 @@ //@run-rustfix -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] +#![allow( + clippy::assertions_on_constants, + clippy::equatable_if_let, + clippy::nonminimal_bool, + clippy::eq_op +)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index e0bef7f9c970d..2c85b68df632c 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -1,5 +1,10 @@ //@run-rustfix -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] +#![allow( + clippy::assertions_on_constants, + clippy::equatable_if_let, + clippy::nonminimal_bool, + clippy::eq_op +)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 6327444df21d7..c687bae1acc52 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -1,5 +1,5 @@ error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:9:5 + --> $DIR/collapsible_if.rs:14:5 | LL | / if x == "hello" { LL | | if y == "world" { @@ -17,7 +17,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:15:5 + --> $DIR/collapsible_if.rs:20:5 | LL | / if x == "hello" || x == "world" { LL | | if y == "world" || y == "hello" { @@ -34,7 +34,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:21:5 + --> $DIR/collapsible_if.rs:26:5 | LL | / if x == "hello" && x == "world" { LL | | if y == "world" || y == "hello" { @@ -51,7 +51,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:27:5 + --> $DIR/collapsible_if.rs:32:5 | LL | / if x == "hello" || x == "world" { LL | | if y == "world" && y == "hello" { @@ -68,7 +68,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:33:5 + --> $DIR/collapsible_if.rs:38:5 | LL | / if x == "hello" && x == "world" { LL | | if y == "world" && y == "hello" { @@ -85,7 +85,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:39:5 + --> $DIR/collapsible_if.rs:44:5 | LL | / if 42 == 1337 { LL | | if 'a' != 'A' { @@ -102,7 +102,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:95:5 + --> $DIR/collapsible_if.rs:100:5 | LL | / if x == "hello" { LL | | if y == "world" { // Collapsible @@ -119,7 +119,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:154:5 + --> $DIR/collapsible_if.rs:159:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} @@ -127,7 +127,7 @@ LL | | } | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:159:5 + --> $DIR/collapsible_if.rs:164:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} diff --git a/tests/ui/dbg_macro.rs b/tests/ui/dbg_macro.rs index 8701e3cd29f47..10788d4048164 100644 --- a/tests/ui/dbg_macro.rs +++ b/tests/ui/dbg_macro.rs @@ -4,6 +4,7 @@ fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } +fn bar(_: ()) {} fn factorial(n: u32) -> u32 { if dbg!(n <= 1) { @@ -21,6 +22,32 @@ fn main() { dbg!(1, 2, 3, 4, 5); } +fn issue9914() { + macro_rules! foo { + ($x:expr) => { + $x; + }; + } + macro_rules! foo2 { + ($x:expr) => { + $x; + }; + } + macro_rules! expand_to_dbg { + () => { + dbg!(); + }; + } + + dbg!(); + #[allow(clippy::let_unit_value)] + let _ = dbg!(); + bar(dbg!()); + foo!(dbg!()); + foo2!(foo!(dbg!())); + expand_to_dbg!(); +} + mod issue7274 { trait Thing<'b> { fn foo(&self); diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index ddb5f1342e994..530e766331777 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -11,7 +11,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:9:8 + --> $DIR/dbg_macro.rs:10:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:10:9 + --> $DIR/dbg_macro.rs:11:9 | LL | dbg!(1) | ^^^^^^^ @@ -33,7 +33,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:12:9 + --> $DIR/dbg_macro.rs:13:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:17:5 + --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(42); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:18:5 + --> $DIR/dbg_macro.rs:19:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:19:14 + --> $DIR/dbg_macro.rs:20:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:20:5 + --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:21:5 + --> $DIR/dbg_macro.rs:22:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,63 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:41:9 + --> $DIR/dbg_macro.rs:42:5 + | +LL | dbg!(); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:44:13 + | +LL | let _ = dbg!(); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | let _ = (); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:45:9 + | +LL | bar(dbg!()); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | bar(()); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:46:10 + | +LL | foo!(dbg!()); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | foo!(()); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:47:16 + | +LL | foo2!(foo!(dbg!())); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | foo2!(foo!(())); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:68:9 | LL | dbg!(2); | ^^^^^^^ @@ -110,7 +166,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:47:5 + --> $DIR/dbg_macro.rs:74:5 | LL | dbg!(1); | ^^^^^^^ @@ -121,7 +177,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:52:5 + --> $DIR/dbg_macro.rs:79:5 | LL | dbg!(1); | ^^^^^^^ @@ -132,7 +188,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:58:9 + --> $DIR/dbg_macro.rs:85:9 | LL | dbg!(1); | ^^^^^^^ @@ -142,5 +198,5 @@ help: remove the invocation before committing it to a version control system LL | 1; | ~ -error: aborting due to 13 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/default_constructed_unit_structs.fixed b/tests/ui/default_constructed_unit_structs.fixed index 4c2d1ea48e119..e1012f38bba2a 100644 --- a/tests/ui/default_constructed_unit_structs.fixed +++ b/tests/ui/default_constructed_unit_structs.fixed @@ -105,6 +105,7 @@ fn main() { // should lint let _ = PhantomData::; let _: PhantomData = PhantomData; + let _: PhantomData = std::marker::PhantomData; let _ = UnitStruct; // should not lint @@ -116,4 +117,21 @@ fn main() { let _ = EmptyStruct::default(); let _ = FakeDefault::default(); let _ = ::default(); + + macro_rules! in_macro { + ($i:ident) => {{ + let _ = UnitStruct::default(); + let _ = $i::default(); + }}; + } + + in_macro!(UnitStruct); + + macro_rules! struct_from_macro { + () => { + UnitStruct + }; + } + + let _ = ::default(); } diff --git a/tests/ui/default_constructed_unit_structs.rs b/tests/ui/default_constructed_unit_structs.rs index 850793dd5de81..c7b4313dbf0c9 100644 --- a/tests/ui/default_constructed_unit_structs.rs +++ b/tests/ui/default_constructed_unit_structs.rs @@ -105,6 +105,7 @@ fn main() { // should lint let _ = PhantomData::::default(); let _: PhantomData = PhantomData::default(); + let _: PhantomData = std::marker::PhantomData::default(); let _ = UnitStruct::default(); // should not lint @@ -116,4 +117,21 @@ fn main() { let _ = EmptyStruct::default(); let _ = FakeDefault::default(); let _ = ::default(); + + macro_rules! in_macro { + ($i:ident) => {{ + let _ = UnitStruct::default(); + let _ = $i::default(); + }}; + } + + in_macro!(UnitStruct); + + macro_rules! struct_from_macro { + () => { + UnitStruct + }; + } + + let _ = ::default(); } diff --git a/tests/ui/default_constructed_unit_structs.stderr b/tests/ui/default_constructed_unit_structs.stderr index 4058943d08727..61a32fb10e53b 100644 --- a/tests/ui/default_constructed_unit_structs.stderr +++ b/tests/ui/default_constructed_unit_structs.stderr @@ -25,10 +25,16 @@ LL | let _: PhantomData = PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:108:23 + --> $DIR/default_constructed_unit_structs.rs:108:55 + | +LL | let _: PhantomData = std::marker::PhantomData::default(); + | ^^^^^^^^^^^ help: remove this call to `default` + +error: use of `default` to create a unit struct + --> $DIR/default_constructed_unit_structs.rs:109:23 | LL | let _ = UnitStruct::default(); | ^^^^^^^^^^^ help: remove this call to `default` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/empty_line_after_doc_comments.rs b/tests/ui/empty_line_after_doc_comments.rs new file mode 100644 index 0000000000000..e843770f57854 --- /dev/null +++ b/tests/ui/empty_line_after_doc_comments.rs @@ -0,0 +1,132 @@ +//@aux-build:proc_macro_attr.rs +#![warn(clippy::empty_line_after_doc_comments)] +#![allow(clippy::assertions_on_constants)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#[macro_use] +extern crate proc_macro_attr; + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } +} + +/// This should produce a warning + +fn with_doc_and_newline() { assert!(true)} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +/// some comment +fn with_one_newline_and_comment() { assert!(true) } + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() { assert!(true) } + + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +fn with_one_newline() { assert!(true) } + +// This should *NOT* produce a warning +#[crate_type = "lib"] + + +fn with_two_newlines() { assert!(true) } + + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +enum Baz { + One, + Two +} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +struct Foo { + one: isize, + two: isize +} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +mod foo { +} + +/// This doc comment should produce a warning + +/** This is also a doc comment and should produce a warning + */ + +// This should *NOT* produce a warning +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(missing_docs)] +fn three_attributes() { assert!(true) } + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4 +} + +// This should *NOT* produce a warning because the empty line is inside a block comment +#[crate_type = "lib"] +/* + +*/ +pub struct S; + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/* test */ +pub struct T; + +// This should *NOT* produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/tests/ui/empty_line_after_doc_comments.stderr b/tests/ui/empty_line_after_doc_comments.stderr new file mode 100644 index 0000000000000..2ca1b51679ed1 --- /dev/null +++ b/tests/ui/empty_line_after_doc_comments.stderr @@ -0,0 +1,36 @@ +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:18:1 + | +LL | / /// This should produce a warning +LL | | +LL | | fn with_doc_and_newline() { assert!(true)} + | |_ + | + = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` + +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:68:1 + | +LL | / /// This doc comment should produce a warning +LL | | +LL | | /** This is also a doc comment and should produce a warning +LL | | */ +... | +LL | | #[allow(missing_docs)] +LL | | fn three_attributes() { assert!(true) } + | |_ + +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:70:1 + | +LL | / /** This is also a doc comment and should produce a warning +LL | | */ +LL | | +LL | | // This should *NOT* produce a warning +... | +LL | | #[allow(missing_docs)] +LL | | fn three_attributes() { assert!(true) } + | |_ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/float_arithmetic.rs b/tests/ui/float_arithmetic.rs index 60fa7569eb9dd..a928c35e8bc99 100644 --- a/tests/ui/float_arithmetic.rs +++ b/tests/ui/float_arithmetic.rs @@ -1,4 +1,4 @@ -#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![warn(clippy::arithmetic_side_effects, clippy::float_arithmetic)] #![allow( unused, clippy::shadow_reuse, diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs deleted file mode 100644 index ab9b6094c2c1d..0000000000000 --- a/tests/ui/integer_arithmetic.rs +++ /dev/null @@ -1,109 +0,0 @@ -//@aux-build:proc_macro_derive.rs - -#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)] - -extern crate proc_macro_derive; - -#[derive(proc_macro_derive::ShadowDerive)] -pub struct Nothing; - -#[rustfmt::skip] -fn main() { - let mut i = 1i32; - let mut var1 = 13i32; - let mut var2 = -1i32; - 1 + i; - i * 2; - 1 % - i / 2; // no error, this is part of the expression in the preceding line - i - 2 + 2 - i; - -i; - i >> 1; - i << 1; - - // no error, overflows are checked by `overflowing_literals` - -1; - -(-1); - - i & 1; // no wrapping - i | 1; - i ^ 1; - - i += 1; - i -= 1; - i *= 2; - i /= 2; - i /= 0; - i /= -1; - i /= var1; - i /= var2; - i %= 2; - i %= 0; - i %= -1; - i %= var1; - i %= var2; - i <<= 3; - i >>= 2; - - // no errors - i |= 1; - i &= 1; - i ^= i; - - // No errors for the following items because they are constant expressions - enum Foo { - Bar = -2, - } - struct Baz([i32; 1 + 1]); - union Qux { - field: [i32; 1 + 1], - } - type Alias = [i32; 1 + 1]; - - const FOO: i32 = -2; - static BAR: i32 = -2; - - let _: [i32; 1 + 1] = [0, 0]; - - let _: [i32; 1 + 1] = { - let a: [i32; 1 + 1] = [0, 0]; - a - }; - - trait Trait { - const ASSOC: i32 = 1 + 1; - } - - impl Trait for Foo { - const ASSOC: i32 = { - let _: [i32; 1 + 1]; - fn foo() {} - 1 + 1 - }; - } -} - -// warn on references as well! (#5328) -pub fn int_arith_ref() { - 3 + &1; - &3 + 1; - &3 + &1; -} - -pub fn foo(x: &i32) -> i32 { - let a = 5; - a + x -} - -pub fn bar(x: &i32, y: &i32) -> i32 { - x + y -} - -pub fn baz(x: i32, y: &i32) -> i32 { - x + y -} - -pub fn qux(x: i32, y: i32) -> i32 { - (&x + &y) -} diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr deleted file mode 100644 index add3b6b90fa26..0000000000000 --- a/tests/ui/integer_arithmetic.stderr +++ /dev/null @@ -1,169 +0,0 @@ -error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:37:5 - | -LL | i /= 0; - | ^^^^^^ attempt to divide `_` by zero - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:42:5 - | -LL | i %= 0; - | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 - | -LL | 1 + i; - | ^^^^^ - | - = note: `-D clippy::integer-arithmetic` implied by `-D warnings` - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:17:5 - | -LL | i * 2; - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:18:5 - | -LL | / 1 % -LL | | i / 2; // no error, this is part of the expression in the preceding line - | |_____^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 - | -LL | i - 2 + 2 - i; - | ^^^^^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 - | -LL | -i; - | ^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:22:5 - | -LL | i >> 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:23:5 - | -LL | i << 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 - | -LL | i += 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 - | -LL | i -= 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 - | -LL | i *= 2; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:38:11 - | -LL | i /= -1; - | ^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:39:5 - | -LL | i /= var1; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:40:5 - | -LL | i /= var2; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:43:11 - | -LL | i %= -1; - | ^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:44:5 - | -LL | i %= var1; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:45:5 - | -LL | i %= var2; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:46:5 - | -LL | i <<= 3; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:47:5 - | -LL | i >>= 2; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:89:5 - | -LL | 3 + &1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:90:5 - | -LL | &3 + 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:91:5 - | -LL | &3 + &1; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:96:5 - | -LL | a + x - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:100:5 - | -LL | x + y - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:104:5 - | -LL | x + y - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:108:5 - | -LL | (&x + &y) - | ^^^^^^^^^ - -error: aborting due to 27 previous errors - diff --git a/tests/ui/let_underscore_untyped.rs b/tests/ui/let_underscore_untyped.rs index 8486137d3a662..2c313ff35d59d 100644 --- a/tests/ui/let_underscore_untyped.rs +++ b/tests/ui/let_underscore_untyped.rs @@ -1,6 +1,12 @@ +//@aux-build: proc_macros.rs + #![allow(unused)] #![warn(clippy::let_underscore_untyped)] +extern crate proc_macros; +use proc_macros::with_span; + +use clippy_utils::is_from_proc_macro; use std::future::Future; use std::{boxed::Box, fmt::Display}; @@ -32,6 +38,14 @@ fn g() -> impl Fn() { || {} } +with_span!( + span + + fn dont_lint_proc_macro() { + let _ = a(); + } +); + fn main() { let _ = a(); let _ = b(1); @@ -40,6 +54,7 @@ fn main() { let _ = e(); let _ = f(); let _ = g(); + let closure = || {}; _ = a(); _ = b(1); diff --git a/tests/ui/let_underscore_untyped.stderr b/tests/ui/let_underscore_untyped.stderr index 6844cb998f72a..bbf2508af10b7 100644 --- a/tests/ui/let_underscore_untyped.stderr +++ b/tests/ui/let_underscore_untyped.stderr @@ -1,60 +1,60 @@ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:36:5 + --> $DIR/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:36:10 + --> $DIR/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ = note: `-D clippy::let-underscore-untyped` implied by `-D warnings` error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:37:5 + --> $DIR/let_underscore_untyped.rs:51:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:37:10 + --> $DIR/let_underscore_untyped.rs:51:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:39:5 + --> $DIR/let_underscore_untyped.rs:53:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:39:10 + --> $DIR/let_underscore_untyped.rs:53:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:40:5 + --> $DIR/let_underscore_untyped.rs:54:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:40:10 + --> $DIR/let_underscore_untyped.rs:54:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:41:5 + --> $DIR/let_underscore_untyped.rs:55:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:41:10 + --> $DIR/let_underscore_untyped.rs:55:10 | LL | let _ = f(); | ^ diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index d175597a44a63..3996d775f55f4 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -8,6 +8,12 @@ )] #![warn(clippy::manual_let_else)] +enum Variant { + A(usize, usize), + B(usize), + C, +} + fn g() -> Option<()> { None } @@ -135,6 +141,15 @@ fn fire() { }; } create_binding_if_some!(w, g()); + + fn e() -> Variant { + Variant::A(0, 0) + } + + // Should not be renamed + let v = if let Variant::A(a, 0) = e() { a } else { return }; + // Should be renamed + let v = if let Variant::B(b) = e() { b } else { return }; } fn not_fire() { diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 52aac6bc673d1..f6f56f7b00e51 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -1,13 +1,13 @@ error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:18:5 + --> $DIR/manual_let_else.rs:24:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` | = note: `-D clippy::manual-let-else` implied by `-D warnings` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:19:5 + --> $DIR/manual_let_else.rs:25:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -18,13 +18,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + return; LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:25:5 + --> $DIR/manual_let_else.rs:31:5 | LL | / let v = if let Some(v) = g() { LL | | // Blocks around the identity should have no impact @@ -45,25 +45,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:38:9 + --> $DIR/manual_let_else.rs:44:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { continue };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:39:9 + --> $DIR/manual_let_else.rs:45:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { break };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:43:5 + --> $DIR/manual_let_else.rs:49:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { panic!() };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:46:5 + --> $DIR/manual_let_else.rs:52:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -74,13 +74,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + std::process::abort() LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:53:5 + --> $DIR/manual_let_else.rs:59:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -91,13 +91,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + if true { return } else { panic!() } LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:60:5 + --> $DIR/manual_let_else.rs:66:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -109,14 +109,14 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + if true {} LL + panic!(); LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:70:5 + --> $DIR/manual_let_else.rs:76:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -129,7 +129,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match () { LL + _ if panic!() => {}, LL + _ => panic!(), @@ -138,13 +138,13 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:80:5 + --> $DIR/manual_let_else.rs:86:5 | LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { if panic!() {} };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { if panic!() {} };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:83:5 + --> $DIR/manual_let_else.rs:89:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -157,7 +157,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match panic!() { LL + _ => {}, LL + } @@ -165,7 +165,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:92:5 + --> $DIR/manual_let_else.rs:98:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -178,7 +178,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { if true { +LL ~ let Some(v) = g() else { if true { LL + return; LL + } else { LL + panic!("diverge"); @@ -186,7 +186,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:101:5 + --> $DIR/manual_let_else.rs:107:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -199,7 +199,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match (g(), g()) { LL + (Some(_), None) => return, LL + (None, Some(_)) => { @@ -215,7 +215,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:118:5 + --> $DIR/manual_let_else.rs:124:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | v_some @@ -226,13 +226,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g().map(|v| (v, 42)) else { +LL ~ let Some((v, w)) = g().map(|v| (v, 42)) else { LL + return; LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:125:5 + --> $DIR/manual_let_else.rs:131:5 | LL | / let v = if let (Some(v_some), w_some) = (g(), 0) { LL | | (w_some, v_some) @@ -249,10 +249,10 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:134:13 + --> $DIR/manual_let_else.rs:140:13 | LL | let $n = if let Some(v) = $e { v } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` ... LL | create_binding_if_some!(w, g()); | ------------------------------- in this macro invocation @@ -260,13 +260,25 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:247:5 + --> $DIR/manual_let_else.rs:150:5 + | +LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:152:5 + | +LL | let v = if let Variant::B(b) = e() { b } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:262:5 | LL | / let _ = match ff { LL | | Some(value) => value, LL | | _ => macro_call!(), LL | | }; - | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };` + | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 18 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 7abaa0b85d23e..bacc14dc96720 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -5,7 +5,7 @@ LL | / let v = match g() { LL | | Some(v_some) => v_some, LL | | None => return, LL | | }; - | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | |______^ help: consider writing: `let Some(v) = g() else { return };` | = note: `-D clippy::manual-let-else` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / let v = match g() { LL | | Some(v_some) => v_some, LL | | _ => return, LL | | }; - | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | |______^ help: consider writing: `let Some(v) = g() else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:44:9 diff --git a/tests/ui/manual_next_back.fixed b/tests/ui/manual_next_back.fixed new file mode 100644 index 0000000000000..e8a47063ad613 --- /dev/null +++ b/tests/ui/manual_next_back.fixed @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_next_back)] + +struct FakeIter(std::ops::Range); + +impl FakeIter { + fn rev(self) -> Self { + self + } + + fn next(&self) {} +} + +impl DoubleEndedIterator for FakeIter { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + +impl Iterator for FakeIter { + type Item = i32; + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn main() { + // should not lint + FakeIter(0..10).rev().next(); + + // should lint + let _ = (0..10).next_back().unwrap(); + let _ = "something".bytes().next_back(); +} diff --git a/tests/ui/manual_next_back.rs b/tests/ui/manual_next_back.rs new file mode 100644 index 0000000000000..9ec89242241c8 --- /dev/null +++ b/tests/ui/manual_next_back.rs @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_next_back)] + +struct FakeIter(std::ops::Range); + +impl FakeIter { + fn rev(self) -> Self { + self + } + + fn next(&self) {} +} + +impl DoubleEndedIterator for FakeIter { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + +impl Iterator for FakeIter { + type Item = i32; + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn main() { + // should not lint + FakeIter(0..10).rev().next(); + + // should lint + let _ = (0..10).rev().next().unwrap(); + let _ = "something".bytes().rev().next(); +} diff --git a/tests/ui/manual_next_back.stderr b/tests/ui/manual_next_back.stderr new file mode 100644 index 0000000000000..94ccaa9e4cc3f --- /dev/null +++ b/tests/ui/manual_next_back.stderr @@ -0,0 +1,16 @@ +error: manual backwards iteration + --> $DIR/manual_next_back.rs:34:20 + | +LL | let _ = (0..10).rev().next().unwrap(); + | ^^^^^^^^^^^^^ help: use: `.next_back()` + | + = note: `-D clippy::manual-next-back` implied by `-D warnings` + +error: manual backwards iteration + --> $DIR/manual_next_back.rs:35:32 + | +LL | let _ = "something".bytes().rev().next(); + | ^^^^^^^^^^^^^ help: use: `.next_back()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 7215660da67f1..60f590661735c 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -15,7 +15,7 @@ fn main() { let _y = matches!(x, Some(0)); // Lint - let _w = matches!(x, Some(_)); + let _w = x.is_some(); // Turn into is_none let _z = x.is_none(); diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 46f67ef4900f8..b72fe10b74804 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -10,7 +10,7 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: match expression looks like `matches!` macro +error: redundant pattern matching, consider using `is_some()` --> $DIR/match_expr_like_matches_macro.rs:21:14 | LL | let _w = match x { @@ -18,7 +18,9 @@ LL | let _w = match x { LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `matches!(x, Some(_))` + | |_____^ help: try this: `x.is_some()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` --> $DIR/match_expr_like_matches_macro.rs:27:14 @@ -29,8 +31,6 @@ LL | | Some(_) => false, LL | | None => true, LL | | }; | |_____^ help: try this: `x.is_none()` - | - = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:33:15 diff --git a/tests/ui/match_same_arms.rs b/tests/ui/match_same_arms.rs index 0b9342c9c4234..3914b45464c71 100644 --- a/tests/ui/match_same_arms.rs +++ b/tests/ui/match_same_arms.rs @@ -53,4 +53,84 @@ mod issue4244 { } } -fn main() {} +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 82b2c433d99e2..60b2975be0454 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -239,4 +239,10 @@ fn main() { 3 => core::convert::identity::(todo!()), _ => 5, }; + + let _ = match 0 { + 0 => cfg!(not_enable), + 1 => cfg!(not_enable), + _ => false, + }; } diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 06cd4300054d6..8fb461bd28667 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -192,5 +192,20 @@ note: other arm here LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:245:9 + | +LL | 1 => cfg!(not_enable), + | -^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `1 | 0` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:244:9 + | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index f860852e7b7b9..bf1911881c8a9 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -63,6 +63,13 @@ fn main() { needless_bool2(x); needless_bool3(x); needless_bool_condition(); + + if a == b { + true + } else { + // Do not lint as this comment might be important + false + }; } fn bool_ret3(x: bool) -> bool { diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index 6680dab5b6dd0..a6c465d4fbd11 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -99,6 +99,13 @@ fn main() { needless_bool2(x); needless_bool3(x); needless_bool_condition(); + + if a == b { + true + } else { + // Do not lint as this comment might be important + false + }; } fn bool_ret3(x: bool) -> bool { diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index d2c48376f7662..fa906374fb3ba 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -91,7 +91,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:105:5 + --> $DIR/fixable.rs:112:5 | LL | / if x { LL | | return true; @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:113:5 + --> $DIR/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -111,7 +111,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:121:5 + --> $DIR/fixable.rs:128:5 | LL | / if x && y { LL | | return true; @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:129:5 + --> $DIR/fixable.rs:136:5 | LL | / if x && y { LL | | return false; @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:137:8 + --> $DIR/fixable.rs:144:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -139,25 +139,25 @@ LL | if x == true {}; = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:141:8 + --> $DIR/fixable.rs:148:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:151:8 + --> $DIR/fixable.rs:158:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:152:8 + --> $DIR/fixable.rs:159:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:161:12 + --> $DIR/fixable.rs:168:12 | LL | } else if returns_bool() { | ____________^ @@ -168,7 +168,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:174:5 + --> $DIR/fixable.rs:181:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -178,13 +178,13 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:179:30 + --> $DIR/fixable.rs:186:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:182:9 + --> $DIR/fixable.rs:189:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 024c22de225da..b7e80af501547 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -62,4 +62,16 @@ fn main() { let _ = sample.iter().next().is_none(); let _ = sample.iter().any(|x| x == &0); + + #[allow(clippy::double_parens)] + { + Vec::::new().extend((0..10)); + foo((0..10)); + bar((0..10).collect::>(), (0..10)); + baz((0..10), (), ('a'..='z')) + } } + +fn foo(_: impl IntoIterator) {} +fn bar>(_: Vec, _: I) {} +fn baz>(_: I, _: (), _: impl IntoIterator) {} diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ed7babec3078..680b6fa5b55f5 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -62,4 +62,16 @@ fn main() { let _ = sample.iter().collect::>().is_empty(); let _ = sample.iter().collect::>().contains(&&0); + + #[allow(clippy::double_parens)] + { + Vec::::new().extend((0..10).collect::>()); + foo((0..10).collect::>()); + bar((0..10).collect::>(), (0..10).collect::>()); + baz((0..10), (), ('a'..='z').collect::>()) + } } + +fn foo(_: impl IntoIterator) {} +fn bar>(_: Vec, _: I) {} +fn baz>(_: I, _: (), _: impl IntoIterator) {} diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 584d2a1d8356f..ad22a7b057e0f 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -90,5 +90,29 @@ error: avoid using `collect()` when not needed LL | let _ = sample.iter().collect::>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` -error: aborting due to 15 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:68:40 + | +LL | Vec::::new().extend((0..10).collect::>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:69:20 + | +LL | foo((0..10).collect::>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:70:49 + | +LL | bar((0..10).collect::>(), (0..10).collect::>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:71:37 + | +LL | baz((0..10), (), ('a'..='z').collect::>()) + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: aborting due to 19 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ee4e5007dc5e9..d49ae5d8636f5 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -231,8 +231,9 @@ fn needless_return_macro() -> String { } fn issue_9361() -> i32 { - #[allow(clippy::integer_arithmetic)] - return 1 + 2; + let n = 1; + #[allow(clippy::arithmetic_side_effects)] + return n + n; } fn issue8336(x: i32) -> bool { diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index cd999db4f40c2..367638261746c 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -239,8 +239,9 @@ fn needless_return_macro() -> String { } fn issue_9361() -> i32 { - #[allow(clippy::integer_arithmetic)] - return 1 + 2; + let n = 1; + #[allow(clippy::arithmetic_side_effects)] + return n + n; } fn issue8336(x: i32) -> bool { diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 87d0cd3e14cfa..05f6038cd2553 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -328,7 +328,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:249:9 + --> $DIR/needless_return.rs:250:9 | LL | return true; | ^^^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:251:9 + --> $DIR/needless_return.rs:252:9 | LL | return false; | ^^^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:13 + --> $DIR/needless_return.rs:259:13 | LL | return 10; | ^^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:261:13 + --> $DIR/needless_return.rs:262:13 | LL | return 100; | ^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:269:9 + --> $DIR/needless_return.rs:270:9 | LL | return 0; | ^^^^^^^^ @@ -368,7 +368,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:276:13 + --> $DIR/needless_return.rs:277:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:278:13 + --> $DIR/needless_return.rs:279:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +384,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:285:20 + --> $DIR/needless_return.rs:286:20 | LL | let _ = 42; | ____________________^ @@ -395,7 +395,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:292:20 + --> $DIR/needless_return.rs:293:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -403,7 +403,7 @@ LL | let _ = 42; return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:304:9 + --> $DIR/needless_return.rs:305:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -411,7 +411,7 @@ LL | return Ok(format!("ok!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:306:9 + --> $DIR/needless_return.rs:307:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL | return Err(format!("err!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:312:9 + --> $DIR/needless_return.rs:313:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | return if true { 1 } else { 2 }; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:316:9 + --> $DIR/needless_return.rs:317:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/non_minimal_cfg.fixed b/tests/ui/non_minimal_cfg.fixed new file mode 100644 index 0000000000000..430caafb33e12 --- /dev/null +++ b/tests/ui/non_minimal_cfg.fixed @@ -0,0 +1,17 @@ +//@run-rustfix + +#![allow(unused)] + +#[cfg(windows)] +fn hermit() {} + +#[cfg(windows)] +fn wasi() {} + +#[cfg(all(unix, not(windows)))] +fn the_end() {} + +#[cfg(any())] +fn any() {} + +fn main() {} diff --git a/tests/ui/non_minimal_cfg.rs b/tests/ui/non_minimal_cfg.rs new file mode 100644 index 0000000000000..a38ce1c21d6e3 --- /dev/null +++ b/tests/ui/non_minimal_cfg.rs @@ -0,0 +1,17 @@ +//@run-rustfix + +#![allow(unused)] + +#[cfg(all(windows))] +fn hermit() {} + +#[cfg(any(windows))] +fn wasi() {} + +#[cfg(all(any(unix), all(not(windows))))] +fn the_end() {} + +#[cfg(any())] +fn any() {} + +fn main() {} diff --git a/tests/ui/non_minimal_cfg.stderr b/tests/ui/non_minimal_cfg.stderr new file mode 100644 index 0000000000000..cdfd728aa6115 --- /dev/null +++ b/tests/ui/non_minimal_cfg.stderr @@ -0,0 +1,28 @@ +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:5:7 + | +LL | #[cfg(all(windows))] + | ^^^^^^^^^^^^ help: try: `windows` + | + = note: `-D clippy::non-minimal-cfg` implied by `-D warnings` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:8:7 + | +LL | #[cfg(any(windows))] + | ^^^^^^^^^^^^ help: try: `windows` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:11:11 + | +LL | #[cfg(all(any(unix), all(not(windows))))] + | ^^^^^^^^^ help: try: `unix` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:11:22 + | +LL | #[cfg(all(any(unix), all(not(windows))))] + | ^^^^^^^^^^^^^^^^^ help: try: `not(windows)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/non_minimal_cfg2.rs b/tests/ui/non_minimal_cfg2.rs new file mode 100644 index 0000000000000..a4c6abce38764 --- /dev/null +++ b/tests/ui/non_minimal_cfg2.rs @@ -0,0 +1,6 @@ +#![allow(unused)] + +#[cfg(all())] +fn all() {} + +fn main() {} diff --git a/tests/ui/non_minimal_cfg2.stderr b/tests/ui/non_minimal_cfg2.stderr new file mode 100644 index 0000000000000..2a9a36fbcef31 --- /dev/null +++ b/tests/ui/non_minimal_cfg2.stderr @@ -0,0 +1,10 @@ +error: unneeded sub `cfg` when there is no condition + --> $DIR/non_minimal_cfg2.rs:3:7 + | +LL | #[cfg(all())] + | ^^^^^ + | + = note: `-D clippy::non-minimal-cfg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 57f341e0276c5..2b8ce5477cc6d 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -92,6 +92,15 @@ fn pattern_to_vec(pattern: &str) -> Vec { .collect::>() } +// #10335 +fn test_result_impure_else(variable: Result) { + variable.map_or_else(|_| { + println!("Err"); + }, |binding| { + println!("Ok {binding}"); + }) +} + enum DummyEnum { One(u8), Two, @@ -113,6 +122,7 @@ fn main() { unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); + test_result_impure_else(Ok(42)); let _ = negative_tests(None); let _ = impure_else(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 19f9f70451796..cfbec8cb27da1 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -115,6 +115,15 @@ fn pattern_to_vec(pattern: &str) -> Vec { .collect::>() } +// #10335 +fn test_result_impure_else(variable: Result) { + if let Ok(binding) = variable { + println!("Ok {binding}"); + } else { + println!("Err"); + } +} + enum DummyEnum { One(u8), Two, @@ -136,6 +145,7 @@ fn main() { unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); + test_result_impure_else(Ok(42)); let _ = negative_tests(None); let _ = impure_else(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index f5e4affb67229..91d52fc79b810 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -152,14 +152,33 @@ LL | | vec![s.to_string()] LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:120:5 + | +LL | / if let Ok(binding) = variable { +LL | | println!("Ok {binding}"); +LL | | } else { +LL | | println!("Err"); +LL | | } + | |_____^ + | +help: try + | +LL ~ variable.map_or_else(|_| { +LL + println!("Err"); +LL + }, |binding| { +LL + println!("Ok {binding}"); +LL + }) + | + error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:133:13 + --> $DIR/option_if_let_else.rs:142:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:142:13 + --> $DIR/option_if_let_else.rs:152:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +200,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:170:13 + --> $DIR/option_if_let_else.rs:180:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:174:13 + --> $DIR/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -207,7 +226,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:213:13 + --> $DIR/option_if_let_else.rs:223:13 | LL | let _ = match s { | _____________^ @@ -217,7 +236,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:217:13 + --> $DIR/option_if_let_else.rs:227:13 | LL | let _ = match Some(10) { | _____________^ @@ -227,7 +246,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:223:13 + --> $DIR/option_if_let_else.rs:233:13 | LL | let _ = match res { | _____________^ @@ -237,7 +256,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:227:13 + --> $DIR/option_if_let_else.rs:237:13 | LL | let _ = match res { | _____________^ @@ -247,10 +266,10 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:231:13 + --> $DIR/option_if_let_else.rs:241:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/partialeq_to_none.fixed b/tests/ui/partialeq_to_none.fixed index 81a716bd276c7..2df87a26d6d15 100644 --- a/tests/ui/partialeq_to_none.fixed +++ b/tests/ui/partialeq_to_none.fixed @@ -1,5 +1,6 @@ //@run-rustfix #![warn(clippy::partialeq_to_none)] +#![allow(clippy::eq_op)] struct Foobar; diff --git a/tests/ui/partialeq_to_none.rs b/tests/ui/partialeq_to_none.rs index f454715fa30f9..df6233b9afd63 100644 --- a/tests/ui/partialeq_to_none.rs +++ b/tests/ui/partialeq_to_none.rs @@ -1,5 +1,6 @@ //@run-rustfix #![warn(clippy::partialeq_to_none)] +#![allow(clippy::eq_op)] struct Foobar; diff --git a/tests/ui/partialeq_to_none.stderr b/tests/ui/partialeq_to_none.stderr index d06ab7aee558b..4f84862a22b8f 100644 --- a/tests/ui/partialeq_to_none.stderr +++ b/tests/ui/partialeq_to_none.stderr @@ -1,5 +1,5 @@ error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:14:8 + --> $DIR/partialeq_to_none.rs:15:8 | LL | if f != None { "yay" } else { "nay" } | ^^^^^^^^^ help: use `Option::is_some()` instead: `f.is_some()` @@ -7,55 +7,55 @@ LL | if f != None { "yay" } else { "nay" } = note: `-D clippy::partialeq-to-none` implied by `-D warnings` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:44:13 + --> $DIR/partialeq_to_none.rs:45:13 | LL | let _ = x == None; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:45:13 + --> $DIR/partialeq_to_none.rs:46:13 | LL | let _ = x != None; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:46:13 + --> $DIR/partialeq_to_none.rs:47:13 | LL | let _ = None == x; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:47:13 + --> $DIR/partialeq_to_none.rs:48:13 | LL | let _ = None != x; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:49:8 + --> $DIR/partialeq_to_none.rs:50:8 | LL | if foobar() == None {} | ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:51:8 + --> $DIR/partialeq_to_none.rs:52:8 | LL | if bar().ok() != None {} | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:53:13 + --> $DIR/partialeq_to_none.rs:54:13 | LL | let _ = Some(1 + 2) != None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:55:13 + --> $DIR/partialeq_to_none.rs:56:13 | LL | let _ = { Some(0) } == None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:57:13 + --> $DIR/partialeq_to_none.rs:58:13 | LL | let _ = { | _____________^ @@ -77,31 +77,31 @@ LL ~ }.is_some(); | error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:67:13 + --> $DIR/partialeq_to_none.rs:68:13 | LL | let _ = optref() == &&None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:68:13 + --> $DIR/partialeq_to_none.rs:69:13 | LL | let _ = &&None != optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:69:13 + --> $DIR/partialeq_to_none.rs:70:13 | LL | let _ = **optref() == None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:70:13 + --> $DIR/partialeq_to_none.rs:71:13 | LL | let _ = &None != *optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:73:13 + --> $DIR/partialeq_to_none.rs:74:13 | LL | let _ = None != *x; | ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()` diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index d62f7d26a35cc..accdf1da9ddc9 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -46,6 +46,7 @@ fn main() { let _ = if opt.is_some() { true } else { false }; issue6067(); + issue10726(); let _ = if gen_opt().is_some() { 1 @@ -88,3 +89,21 @@ fn issue7921() { if (&None::<()>).is_none() {} if (&None::<()>).is_none() {} } + +fn issue10726() { + let x = Some(42); + + x.is_some(); + + x.is_none(); + + x.is_none(); + + x.is_some(); + + // Don't lint + match x { + Some(21) => true, + _ => false, + }; +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index d64294265731b..ec684bdf71c11 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -55,6 +55,7 @@ fn main() { let _ = if let Some(_) = opt { true } else { false }; issue6067(); + issue10726(); let _ = if let Some(_) = gen_opt() { 1 @@ -103,3 +104,33 @@ fn issue7921() { if let None = *(&None::<()>) {} if let None = *&None::<()> {} } + +fn issue10726() { + let x = Some(42); + + match x { + Some(_) => true, + _ => false, + }; + + match x { + None => true, + _ => false, + }; + + match x { + Some(_) => false, + _ => true, + }; + + match x { + None => false, + _ => true, + }; + + // Don't lint + match x { + Some(21) => true, + _ => false, + }; +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 7c5a047e455cf..a69eb3905205f 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -77,49 +77,49 @@ LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:59:20 + --> $DIR/redundant_pattern_matching_option.rs:60:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:61:19 + --> $DIR/redundant_pattern_matching_option.rs:62:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:67:12 + --> $DIR/redundant_pattern_matching_option.rs:68:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:82:12 + --> $DIR/redundant_pattern_matching_option.rs:83:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:84:12 + --> $DIR/redundant_pattern_matching_option.rs:85:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:86:15 + --> $DIR/redundant_pattern_matching_option.rs:87:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:88:15 + --> $DIR/redundant_pattern_matching_option.rs:89:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:90:5 + --> $DIR/redundant_pattern_matching_option.rs:91:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:95:5 + --> $DIR/redundant_pattern_matching_option.rs:96:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,16 +137,52 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:103:12 + --> $DIR/redundant_pattern_matching_option.rs:104:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:104:12 + --> $DIR/redundant_pattern_matching_option.rs:105:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` -error: aborting due to 22 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:111:5 + | +LL | / match x { +LL | | Some(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:116:5 + | +LL | / match x { +LL | | None => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:121:5 + | +LL | / match x { +LL | | Some(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:126:5 + | +LL | / match x { +LL | | None => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_some()` + +error: aborting due to 26 previous errors diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index c48d1522935c5..e4032ae44b715 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -43,6 +43,7 @@ fn main() { issue5504(); issue6067(); issue6065(); + issue10726(); let _ = if gen_res().is_ok() { 1 @@ -107,3 +108,28 @@ const fn issue6067() { Err::(42).is_err(); } + +fn issue10726() { + // This is optional, but it makes the examples easier + let x: Result = Ok(42); + + x.is_ok(); + + x.is_err(); + + x.is_err(); + + x.is_ok(); + + // Don't lint + match x { + Err(16) => false, + _ => true, + }; + + // Don't lint + match x { + Ok(16) => false, + _ => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 26f37d169fac6..39eb10df8789e 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -55,6 +55,7 @@ fn main() { issue5504(); issue6067(); issue6065(); + issue10726(); let _ = if let Ok(_) = gen_res() { 1 @@ -125,3 +126,40 @@ const fn issue6067() { Err(_) => true, }; } + +fn issue10726() { + // This is optional, but it makes the examples easier + let x: Result = Ok(42); + + match x { + Ok(_) => true, + _ => false, + }; + + match x { + Ok(_) => false, + _ => true, + }; + + match x { + Err(_) => true, + _ => false, + }; + + match x { + Err(_) => false, + _ => true, + }; + + // Don't lint + match x { + Err(16) => false, + _ => true, + }; + + // Don't lint + match x { + Ok(16) => false, + _ => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d6a46babb7795..5893ae4dcc492 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:59:20 + --> $DIR/redundant_pattern_matching_result.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:61:19 + --> $DIR/redundant_pattern_matching_result.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:84:19 + --> $DIR/redundant_pattern_matching_result.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:16 + --> $DIR/redundant_pattern_matching_result.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:91:12 + --> $DIR/redundant_pattern_matching_result.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:15 + --> $DIR/redundant_pattern_matching_result.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:110:12 + --> $DIR/redundant_pattern_matching_result.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:112:12 + --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:114:15 + --> $DIR/redundant_pattern_matching_result.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:116:15 + --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:118:5 + --> $DIR/redundant_pattern_matching_result.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:123:5 + --> $DIR/redundant_pattern_matching_result.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -150,5 +150,41 @@ LL | | Err(_) => true, LL | | }; | |_____^ help: try this: `Err::(42).is_err()` -error: aborting due to 22 previous errors +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:134:5 + | +LL | / match x { +LL | | Ok(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:139:5 + | +LL | / match x { +LL | | Ok(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:144:5 + | +LL | / match x { +LL | | Err(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:149:5 + | +LL | / match x { +LL | | Err(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_ok()` + +error: aborting due to 26 previous errors diff --git a/tests/ui/ref_patterns.rs b/tests/ui/ref_patterns.rs new file mode 100644 index 0000000000000..c51e0bc76efda --- /dev/null +++ b/tests/ui/ref_patterns.rs @@ -0,0 +1,19 @@ +#![allow(unused)] +#![warn(clippy::ref_patterns)] + +fn use_in_pattern() { + let opt = Some(5); + match opt { + None => {}, + Some(ref opt) => {}, + } +} + +fn use_in_binding() { + let x = 5; + let ref y = x; +} + +fn use_in_parameter(ref x: i32) {} + +fn main() {} diff --git a/tests/ui/ref_patterns.stderr b/tests/ui/ref_patterns.stderr new file mode 100644 index 0000000000000..aa007782683a6 --- /dev/null +++ b/tests/ui/ref_patterns.stderr @@ -0,0 +1,27 @@ +error: usage of ref pattern + --> $DIR/ref_patterns.rs:8:14 + | +LL | Some(ref opt) => {}, + | ^^^^^^^ + | + = help: consider using `&` for clarity instead + = note: `-D clippy::ref-patterns` implied by `-D warnings` + +error: usage of ref pattern + --> $DIR/ref_patterns.rs:14:9 + | +LL | let ref y = x; + | ^^^^^ + | + = help: consider using `&` for clarity instead + +error: usage of ref pattern + --> $DIR/ref_patterns.rs:17:21 + | +LL | fn use_in_parameter(ref x: i32) {} + | ^^^^^ + | + = help: consider using `&` for clarity instead + +error: aborting due to 3 previous errors + diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index ab8ac97a0e707..a5f79b139bc2a 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -34,8 +34,10 @@ fn syntax_error() { let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + // These following three cases are considering valid since regex-1.8.0 let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + let _ = Regex::new(r"(?hi)").unwrap(); let escaped_string_span = Regex::new("\\b\\c"); diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index c2440f39e0a03..6b8a772e7f0d5 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -82,23 +82,11 @@ error: regex parse error: LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error: unrecognized escape sequence - --> $DIR/regex.rs:37:45 - | -LL | let raw_string_error = Regex::new(r"[...//...]"); - | ^^ - -error: regex syntax error: unrecognized escape sequence - --> $DIR/regex.rs:38:46 - | -LL | let raw_string_error = Regex::new(r#"[...//...]"#); - | ^^ - error: regex parse error: /b/c ^^ error: unrecognized escape sequence - --> $DIR/regex.rs:40:42 + --> $DIR/regex.rs:42:42 | LL | let escaped_string_span = Regex::new("/b/c"); | ^^^^^^^^ @@ -106,13 +94,13 @@ LL | let escaped_string_span = Regex::new("/b/c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> $DIR/regex.rs:42:34 + --> $DIR/regex.rs:44:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: trivial regex - --> $DIR/regex.rs:46:33 + --> $DIR/regex.rs:48:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -120,7 +108,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:48:48 + --> $DIR/regex.rs:50:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -128,7 +116,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:50:42 + --> $DIR/regex.rs:52:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -136,7 +124,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:52:40 + --> $DIR/regex.rs:54:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -144,7 +132,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:54:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -152,7 +140,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:56:39 + --> $DIR/regex.rs:58:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -160,7 +148,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:58:40 + --> $DIR/regex.rs:60:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -168,7 +156,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -176,7 +164,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:63:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -184,7 +172,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:65:36 + --> $DIR/regex.rs:67:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -192,12 +180,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:67:44 + --> $DIR/regex.rs:69:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 25 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 9036f89261288..7c2acf43fe836 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -16,6 +16,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] @@ -58,6 +59,7 @@ #![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] +#![warn(clippy::arithmetic_side_effects)] #![warn(clippy::overly_complex_bool_expr)] #![warn(clippy::new_without_default)] #![warn(clippy::bind_instead_of_map)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 43cabe810f344..8d334b0d0501a 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -16,6 +16,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] @@ -58,6 +59,7 @@ #![warn(clippy::eval_order_dependence)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] +#![warn(clippy::integer_arithmetic)] #![warn(clippy::logic_bug)] #![warn(clippy::new_without_default_derive)] #![warn(clippy::option_and_then_some)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 1ad7cf412c896..fbf8d3d7e4e8b 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,280 +7,286 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` + --> $DIR/rename.rs:62:9 + | +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` + error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `drop_copy` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `drop_copy` error: lint `clippy::drop_ref` has been renamed to `drop_ref` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `drop_ref` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forget_copy` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_copy` error: lint `clippy::forget_ref` has been renamed to `forget_ref` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_ref` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 47 previous errors +error: aborting due to 48 previous errors diff --git a/tests/ui/trait_duplication_in_bounds.fixed b/tests/ui/trait_duplication_in_bounds.fixed index eef8024b131cb..fdac0e4cb1e83 100644 --- a/tests/ui/trait_duplication_in_bounds.fixed +++ b/tests/ui/trait_duplication_in_bounds.fixed @@ -2,6 +2,8 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +use std::any::Any; + fn bad_foo(arg0: T, argo1: U) { unimplemented!(); } @@ -109,4 +111,12 @@ fn qualified_path(arg0: T) { unimplemented!(); } +fn good_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + +fn bad_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index a7a1caf2880d3..a0300da555588 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -2,6 +2,8 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +use std::any::Any; + fn bad_foo(arg0: T, argo1: U) { unimplemented!(); } @@ -109,4 +111,12 @@ fn qualified_path(arg0: T) { unimplemented!(); } +fn good_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + +fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { + unimplemented!(); +} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index af800ba78880c..539b6114ca3ae 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -1,5 +1,5 @@ error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:5:15 + --> $DIR/trait_duplication_in_bounds.rs:7:15 | LL | fn bad_foo(arg0: T, argo1: U) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` @@ -11,46 +11,52 @@ LL | #![deny(clippy::trait_duplication_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:11:8 + --> $DIR/trait_duplication_in_bounds.rs:13:8 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:39:26 + --> $DIR/trait_duplication_in_bounds.rs:41:26 | LL | trait BadSelfTraitBound: Clone + Clone + Clone { | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:46:15 + --> $DIR/trait_duplication_in_bounds.rs:48:15 | LL | Self: Clone + Clone + Clone; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:60:24 + --> $DIR/trait_duplication_in_bounds.rs:62:24 | LL | trait BadTraitBound { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:67:12 + --> $DIR/trait_duplication_in_bounds.rs:69:12 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:100:19 + --> $DIR/trait_duplication_in_bounds.rs:102:19 | LL | fn bad_generic + GenericTrait + GenericTrait>(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait + GenericTrait` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:108:22 + --> $DIR/trait_duplication_in_bounds.rs:110:22 | LL | fn qualified_path(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone` -error: aborting due to 8 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:118:33 + | +LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { + | ^^^^^^^^^^^^^^^^^ help: try: `Any + Send` + +error: aborting due to 9 previous errors diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 01eb6c5b080dd..c16caa38fe93e 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -33,6 +33,11 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn dont_lint_on_type_alias() { + type A = i32; + _ = A::from(0i32); +} + fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); @@ -106,6 +111,7 @@ fn main() { test_questionmark().unwrap(); test_issue_3913().unwrap(); + dont_lint_on_type_alias(); dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); lint_into_iter_on_expr_implementing_iterator(); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 34b43a6299b2f..c75a2bce4ca23 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -33,6 +33,11 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn dont_lint_on_type_alias() { + type A = i32; + _ = A::from(0i32); +} + fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); @@ -106,6 +111,7 @@ fn main() { test_questionmark().unwrap(); test_issue_3913().unwrap(); + dont_lint_on_type_alias(); dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); lint_into_iter_on_expr_implementing_iterator(); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index be067c6843ace..4dca3aac53361 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -23,97 +23,97 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:45:22 + --> $DIR/useless_conversion.rs:50:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:50:21 + --> $DIR/useless_conversion.rs:55:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:56:22 + --> $DIR/useless_conversion.rs:61:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range` - --> $DIR/useless_conversion.rs:62:13 + --> $DIR/useless_conversion.rs:67:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range` - --> $DIR/useless_conversion.rs:67:17 + --> $DIR/useless_conversion.rs:72:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:128:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:129:21 + --> $DIR/useless_conversion.rs:135:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:130:13 + --> $DIR/useless_conversion.rs:136:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:131:13 + --> $DIR/useless_conversion.rs:137:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:132:13 + --> $DIR/useless_conversion.rs:138:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:133:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:134:21 + --> $DIR/useless_conversion.rs:140:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:139:13 + --> $DIR/useless_conversion.rs:145:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:145:23 + --> $DIR/useless_conversion.rs:151:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:147:13 + --> $DIR/useless_conversion.rs:153:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> $DIR/useless_conversion.rs:149:13 + --> $DIR/useless_conversion.rs:155:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` diff --git a/tests/ui/wildcard_imports_cfgtest.rs b/tests/ui/wildcard_imports_cfgtest.rs new file mode 100644 index 0000000000000..203c4e15b50c3 --- /dev/null +++ b/tests/ui/wildcard_imports_cfgtest.rs @@ -0,0 +1,19 @@ +//@compile-flags: --test + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] + +// Test for #10580, the lint should ignore it because of the crate's cfg test flag. + +fn foofoo() {} + +mod outer { + mod inner { + use super::super::*; + fn barbar() { + let _ = foofoo(); + } + } +} + +fn main() {} diff --git a/triagebot.toml b/triagebot.toml index 3f8f6a7b98c21..c40b71f6ca7d4 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -17,9 +17,9 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB [assign.owners] "/.github" = ["@flip1995"] +"/book" = ["@flip1995"] "/util/gh-pages" = ["@xFrednet"] "*" = [ - "@flip1995", "@Manishearth", "@llogiq", "@giraffate", From 91f4fbe5961ad444076c28bfbb37a381d76543cd Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sun, 21 May 2023 02:02:21 +1000 Subject: [PATCH 454/806] Improve comments --- crates/stdx/src/thread.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 2bf9141cbf99b..46d797bbb747c 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -62,8 +62,7 @@ impl Builder { pub struct JoinHandle { // `inner` is an `Option` so that we can - // take ownership of the contained `JoinHandle` - // in the `Drop` impl below. + // take ownership of the contained `JoinHandle`. inner: Option>, allow_leak: bool, } @@ -93,8 +92,8 @@ impl fmt::Debug for JoinHandle { } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +// Please maintain order from least to most priority for the derived `Ord` impl. pub enum QoSClass { - // Maintain order in priority from least to most. Background, Utility, UserInitiated, @@ -132,9 +131,8 @@ pub fn set_current_thread_qos_class(class: QoSClass) { // due to a previous call to a function such as `pthread_setschedparam` // which is incompatible with QoS. // - // Let’s just panic here because rust-analyzer as a system - // should have the property that QoS is used consistently - // instead of old manual scheduling management APIs. + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") } @@ -187,8 +185,14 @@ pub fn get_current_thread_qos_class() -> Option { libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), + libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { - // We panic here because rust-analyzer should never use + // Using manual scheduling APIs causes threads to “opt out” of QoS. + // At this point they become incompatible with QoS, + // and as such have the “unspecified” QoS class. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. panic!("tried to get QoS of thread which has opted out of QoS") } } From bb02ae7532b9c6a99eca129b146039dc5707c17e Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sun, 21 May 2023 02:02:30 +1000 Subject: [PATCH 455/806] Add doc comments for `QoSClass` --- crates/stdx/src/thread.rs | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 46d797bbb747c..bbf0c6b82b346 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -94,9 +94,116 @@ impl fmt::Debug for JoinHandle { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] // Please maintain order from least to most priority for the derived `Ord` impl. pub enum QoSClass { + // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55 + // + /// TLDR: invisible maintenance tasks + /// + /// Contract: + /// + /// * **You do not care about how long it takes for work to finish.** + /// * **You do not care about work being deferred temporarily.** + /// (e.g. if the device’s battery is in a critical state) + /// + /// Examples: + /// + /// * in a video editor: + /// creating periodic backups of project files + /// * in a browser: + /// cleaning up cached sites which have not been accessed in a long time + /// * in a collaborative word processor: + /// creating a searchable index of all documents + /// + /// Use this QoS class for background tasks + /// which the user did not initiate themselves + /// and which are invisible to the user. + /// It is expected that this work will take significant time to complete: + /// minutes or even hours. + /// + /// This QoS class provides the most energy and thermally-efficient execution possible. + /// All other work is prioritized over background tasks. Background, + + /// TLDR: tasks that don’t block using your app + /// + /// Contract: + /// + /// * **Your app remains useful even as the task is executing.** + /// + /// Examples: + /// + /// * in a video editor: + /// exporting a video to disk – + /// the user can still work on the timeline + /// * in a browser: + /// automatically extracting a downloaded zip file – + /// the user can still switch tabs + /// * in a collaborative word processor: + /// downloading images embedded in a document – + /// the user can still make edits + /// + /// Use this QoS class for tasks which + /// may or may not be initiated by the user, + /// but whose result is visible. + /// It is expected that this work will take a few seconds to a few minutes. + /// Typically your app will include a progress bar + /// for tasks using this class. + /// + /// This QoS class provides a balance between + /// performance, responsiveness and efficiency. Utility, + + /// TLDR: tasks that block using your app + /// + /// Contract: + /// + /// * **You need this work to complete + /// before the user can keep interacting with your app.** + /// * **Your work will not take more than a few seconds to complete.** + /// + /// Examples: + /// + /// * in a video editor: + /// opening a saved project + /// * in a browser: + /// loading a list of the user’s bookmarks and top sites + /// when a new tab is created + /// * in a collaborative word processor: + /// running a search on the document’s content + /// + /// Use this QoS class for tasks which were initiated by the user + /// and block the usage of your app while they are in progress. + /// It is expected that this work will take a few seconds or less to complete; + /// not long enough to cause the user to switch to something else. + /// Your app will likely indicate progress on these tasks + /// through the display of placeholder content or modals. + /// + /// This QoS class is not energy-efficient. + /// Rather, it provides responsiveness + /// by prioritizing work above other tasks on the system + /// except for critical user-interactive work. UserInitiated, + + /// TLDR: render loops and nothing else + /// + /// Contract: + /// + /// * **You absolutely need this work to complete immediately + /// or your app will appear to freeze.** + /// * **Your work will always complete virtually instantaneously.** + /// + /// Examples: + /// + /// * the main thread in a GUI application + /// * the update & render loop in a game + /// * a secondary thread which progresses an animation + /// + /// Use this QoS class for any work which, if delayed, + /// will make your user interface unresponsive. + /// It is expected that this work will be virtually instantaneous. + /// + /// This QoS class is not energy-efficient. + /// Specifying this class is a request to run with + /// nearly all available system CPU and I/O bandwidth even under contention. UserInteractive, } From a41624829684b6c5221b0d68a9dc99e8bd8ab241 Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sun, 21 May 2023 02:27:49 +1000 Subject: [PATCH 456/806] Extract platform-specific QoS code into module --- crates/stdx/src/thread.rs | 173 +++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 79 deletions(-) diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index bbf0c6b82b346..5042f001435e8 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -207,105 +207,120 @@ pub enum QoSClass { UserInteractive, } -#[cfg(target_vendor = "apple")] -pub const IS_QOS_AVAILABLE: bool = true; +pub const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE; -#[cfg(not(target_vendor = "apple"))] -pub const IS_QOS_AVAILABLE: bool = false; +pub fn set_current_thread_qos_class(class: QoSClass) { + imp::set_current_thread_qos_class(class) +} + +pub fn get_current_thread_qos_class() -> Option { + imp::get_current_thread_qos_class() +} // All Apple platforms use XNU as their kernel // and thus have the concept of QoS. #[cfg(target_vendor = "apple")] -pub fn set_current_thread_qos_class(class: QoSClass) { - let c = match class { - QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, - QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, - QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, - QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, - }; +mod imp { + use super::QoSClass; - let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; + pub(super) const IS_QOS_AVAILABLE: bool = true; - if code == 0 { - return; - } + pub(super) fn set_current_thread_qos_class(class: QoSClass) { + let c = match class { + QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, + QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, + QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, + QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, + }; - let errno = unsafe { *libc::__error() }; + let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; - match errno { - libc::EPERM => { - // This thread has been excluded from the QoS system - // due to a previous call to a function such as `pthread_setschedparam` - // which is incompatible with QoS. - // - // Panic instead of returning an error - // to maintain the invariant that we only use QoS APIs. - panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") + if code == 0 { + return; + } + + let errno = unsafe { *libc::__error() }; + + match errno { + libc::EPERM => { + // This thread has been excluded from the QoS system + // due to a previous call to a function such as `pthread_setschedparam` + // which is incompatible with QoS. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") + } + + libc::EINVAL => { + // This is returned if we pass something other than a qos_class_t + // to `pthread_set_qos_class_self_np`. + // + // This is impossible, so again panic. + unreachable!( + "invalid qos_class_t value was passed to pthread_set_qos_class_self_np" + ) + } + + _ => { + // `pthread_set_qos_class_self_np`’s documentation + // does not mention any other errors. + unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") + } } + } - libc::EINVAL => { - // This is returned if we pass something other than a qos_class_t - // to `pthread_set_qos_class_self_np`. + pub(super) fn get_current_thread_qos_class() -> Option { + let current_thread = unsafe { libc::pthread_self() }; + let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; + let code = unsafe { + libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) + }; + + if code != 0 { + // `pthread_get_qos_class_np`’s documentation states that + // an error value is placed into errno if the return code is not zero. + // However, it never states what errors are possible. + // Inspecting the source[0] shows that, as of this writing, it always returns zero. + // + // Whatever errors the function could report in future are likely to be + // ones which we cannot handle anyway // - // This is impossible, so again panic. - unreachable!("invalid qos_class_t value was passed to pthread_set_qos_class_self_np") + // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 + let errno = unsafe { *libc::__error() }; + unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); } - _ => { - // `pthread_set_qos_class_self_np`’s documentation - // does not mention any other errors. - unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") + match qos_class_raw { + libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), + libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), + libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set + libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), + libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), + + libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { + // Using manual scheduling APIs causes threads to “opt out” of QoS. + // At this point they become incompatible with QoS, + // and as such have the “unspecified” QoS class. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to get QoS of thread which has opted out of QoS") + } } } } +// FIXME: Windows has QoS APIs, we should use them! #[cfg(not(target_vendor = "apple"))] -pub fn set_current_thread_qos_class(class: QoSClass) { - // FIXME: Windows has QoS APIs, we should use them! -} +mod imp { + use super::QoSClass; -#[cfg(target_vendor = "apple")] -pub fn get_current_thread_qos_class() -> Option { - let current_thread = unsafe { libc::pthread_self() }; - let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; - let code = unsafe { - libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) - }; - - if code != 0 { - // `pthread_get_qos_class_np`’s documentation states that - // an error value is placed into errno if the return code is not zero. - // However, it never states what errors are possible. - // Inspecting the source[0] shows that, as of this writing, it always returns zero. - // - // Whatever errors the function could report in future are likely to be - // ones which we cannot handle anyway - // - // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 - let errno = unsafe { *libc::__error() }; - unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); - } + pub(super) const IS_QOS_AVAILABLE: bool = false; - match qos_class_raw { - libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), - libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), - libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set - libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), - libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), - - libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { - // Using manual scheduling APIs causes threads to “opt out” of QoS. - // At this point they become incompatible with QoS, - // and as such have the “unspecified” QoS class. - // - // Panic instead of returning an error - // to maintain the invariant that we only use QoS APIs. - panic!("tried to get QoS of thread which has opted out of QoS") - } - } -} + pub(super) fn set_current_thread_qos_class(_: QoSClass) {} -#[cfg(not(target_vendor = "apple"))] -pub fn get_current_thread_qos_class() -> Option { - None + pub(super) fn get_current_thread_qos_class() -> Option { + None + } } From 7b70988d48373f6d43874346c1cbcd15712a88c7 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 21 May 2023 19:51:58 +0900 Subject: [PATCH 457/806] fix: consider all tokens in macro expr when analyzing locals Also consider `self` token along with ordinary identifiers. --- .../src/handlers/extract_function.rs | 93 +++++++++++++++++-- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 58605f7ed5705..c29721b464878 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -707,7 +707,7 @@ impl FunctionBody { ) -> (FxIndexSet, Option) { let mut self_param = None; let mut res = FxIndexSet::default(); - let mut cb = |name_ref: Option<_>| { + let mut add_name_if_local = |name_ref: Option<_>| { let local_ref = match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) { Some( @@ -731,21 +731,24 @@ impl FunctionBody { }; self.walk_expr(&mut |expr| match expr { ast::Expr::PathExpr(path_expr) => { - cb(path_expr.path().and_then(|it| it.as_single_name_ref())) + add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref())) } ast::Expr::ClosureExpr(closure_expr) => { if let Some(body) = closure_expr.body() { - body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it)); + body.syntax() + .descendants() + .map(ast::NameRef::cast) + .for_each(&mut add_name_if_local); } } ast::Expr::MacroExpr(expr) => { if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) { tt.syntax() - .children_with_tokens() - .flat_map(SyntaxElement::into_token) - .filter(|it| it.kind() == SyntaxKind::IDENT) + .descendants_with_tokens() + .filter_map(SyntaxElement::into_token) + .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) .flat_map(|t| sema.descend_into_macros(t)) - .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast))); + .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } _ => (), @@ -4344,6 +4347,82 @@ fn $0fun_name(n: i32) -> i32 { ); } + #[test] + fn param_usage_in_macro_with_nested_tt() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + $0let k = n * m!((n) + { t });$0 + let m = k + 1; +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + let k = fun_name(n, t); + let m = k + 1; +} + +fn $0fun_name(n: i32, t: i32) -> i32 { + let k = n * m!((n) + { t }); + k +} +"#, + ) + } + + #[test] + fn param_usage_in_macro_with_nested_tt_2() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + $0let k = n * m!((n) + { self.0 });$0 + let m = k + 1; + } +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + let k = self.fun_name(n); + let m = k + 1; + } + + fn $0fun_name(&self, n: i32) -> i32 { + let k = n * m!((n) + { self.0 }); + k + } +} +"#, + ) + } + #[test] fn extract_with_await() { check_assist( From 9b97ae98f83c425ce62fba4a8d6ffe353d530d9d Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 19 May 2023 11:14:55 +0200 Subject: [PATCH 458/806] Rename `drop_copy` lint to `dropping_copy_types` --- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/renamed_lints.rs | 2 +- tests/ui/multiple_unsafe_ops_per_block.rs | 2 +- tests/ui/rename.fixed | 4 ++-- tests/ui/rename.rs | 2 +- tests/ui/rename.stderr | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index b2f7d026cc8b2..9a7b39e1544eb 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - // early return for uplifted lints: drop_ref, drop_copy, forget_ref, forget_copy + // early return for uplifted lints: drop_ref, dropping_copy_types, forget_ref, forget_copy sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return, sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index a2c3465cde4a3..308e40abf6a25 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -33,7 +33,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), ("clippy::drop_bounds", "drop_bounds"), - ("clippy::drop_copy", "drop_copy"), + ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "drop_ref"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index f28153e56b0fe..4ef6f0ca92f2d 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -2,7 +2,7 @@ #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 7c2acf43fe836..8633e422805a6 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -30,7 +30,7 @@ #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #![allow(drop_ref)] #![allow(for_loops_over_fallibles)] #![allow(forget_copy)] @@ -77,7 +77,7 @@ #![warn(clippy::invisible_characters)] #![warn(suspicious_double_ref_op)] #![warn(drop_bounds)] -#![warn(drop_copy)] +#![warn(dropping_copy_types)] #![warn(drop_ref)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 8d334b0d0501a..07b33b4da95b3 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -30,7 +30,7 @@ #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #![allow(drop_ref)] #![allow(for_loops_over_fallibles)] #![allow(forget_copy)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index fbf8d3d7e4e8b..20e7d96b7209b 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -186,11 +186,11 @@ error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::drop_copy` has been renamed to `drop_copy` +error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::drop_copy)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `drop_copy` + | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `drop_ref` --> $DIR/rename.rs:81:9 From 234f2b67cb7435734c697517eac2afa154b35b0e Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 19 May 2023 11:19:31 +0200 Subject: [PATCH 459/806] Rename `forget_copy` lint to `forgetting_copy_types` --- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/renamed_lints.rs | 2 +- tests/ui/mem_forget.rs | 2 +- tests/ui/rename.fixed | 4 ++-- tests/ui/rename.rs | 2 +- tests/ui/rename.stderr | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 9a7b39e1544eb..db1b8494890ec 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - // early return for uplifted lints: drop_ref, dropping_copy_types, forget_ref, forget_copy + // early return for uplifted lints: drop_ref, dropping_copy_types, forget_ref, forgetting_copy_types sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return, sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 308e40abf6a25..a81dbb9b25992 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -38,7 +38,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - ("clippy::forget_copy", "forget_copy"), + ("clippy::forget_copy", "forgetting_copy_types"), ("clippy::forget_ref", "forget_ref"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), diff --git a/tests/ui/mem_forget.rs b/tests/ui/mem_forget.rs index 5137448a6d4ba..edb9d87d032ec 100644 --- a/tests/ui/mem_forget.rs +++ b/tests/ui/mem_forget.rs @@ -5,7 +5,7 @@ use std::mem as memstuff; use std::mem::forget as forgetSomething; #[warn(clippy::mem_forget)] -#[allow(forget_copy)] +#[allow(forgetting_copy_types)] fn main() { let five: i32 = 5; forgetSomething(five); diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 8633e422805a6..8df63fd9b2335 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -33,7 +33,7 @@ #![allow(dropping_copy_types)] #![allow(drop_ref)] #![allow(for_loops_over_fallibles)] -#![allow(forget_copy)] +#![allow(forgetting_copy_types)] #![allow(forget_ref)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] @@ -82,7 +82,7 @@ #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] -#![warn(forget_copy)] +#![warn(forgetting_copy_types)] #![warn(forget_ref)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 07b33b4da95b3..7206e19f1e021 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -33,7 +33,7 @@ #![allow(dropping_copy_types)] #![allow(drop_ref)] #![allow(for_loops_over_fallibles)] -#![allow(forget_copy)] +#![allow(forgetting_copy_types)] #![allow(forget_ref)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 20e7d96b7209b..c2a7c0dd7eb13 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -216,11 +216,11 @@ error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_ov LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::forget_copy` has been renamed to `forget_copy` +error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::forget_copy)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_copy` + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forget_ref` --> $DIR/rename.rs:86:9 From fdbfe0e249b5d1eb4b4fb508e88bedd505ce0817 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 19 May 2023 11:25:35 +0200 Subject: [PATCH 460/806] Rename `drop_ref` lint to `dropping_references` --- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/renamed_lints.rs | 2 +- tests/ui/rename.fixed | 4 ++-- tests/ui/rename.rs | 2 +- tests/ui/rename.stderr | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index db1b8494890ec..00211d65094ee 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - // early return for uplifted lints: drop_ref, dropping_copy_types, forget_ref, forgetting_copy_types + // early return for uplifted lints: dropping_references, dropping_copy_types, forget_ref, forgetting_copy_types sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return, sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index a81dbb9b25992..4d1a462d110ca 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -34,7 +34,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::clone_double_ref", "suspicious_double_ref_op"), ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), - ("clippy::drop_ref", "drop_ref"), + ("clippy::drop_ref", "dropping_references"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 8df63fd9b2335..a4fb4fbffdfc8 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -31,7 +31,7 @@ #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] -#![allow(drop_ref)] +#![allow(dropping_references)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forget_ref)] @@ -78,7 +78,7 @@ #![warn(suspicious_double_ref_op)] #![warn(drop_bounds)] #![warn(dropping_copy_types)] -#![warn(drop_ref)] +#![warn(dropping_references)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 7206e19f1e021..bb833c9534688 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -31,7 +31,7 @@ #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] -#![allow(drop_ref)] +#![allow(dropping_references)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forget_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index c2a7c0dd7eb13..8bbd046bbf31b 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -192,11 +192,11 @@ error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` -error: lint `clippy::drop_ref` has been renamed to `drop_ref` +error: lint `clippy::drop_ref` has been renamed to `dropping_references` --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::drop_ref)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `drop_ref` + | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:82:9 From 915ff71b7c1448e822889d15ecd6b25c26ac2ed4 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 19 May 2023 11:30:11 +0200 Subject: [PATCH 461/806] Rename `forget_ref` lint to `forgetting_references` --- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/renamed_lints.rs | 2 +- tests/ui/rename.fixed | 4 ++-- tests/ui/rename.rs | 2 +- tests/ui/rename.stderr | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 00211d65094ee..9c60edb179415 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - // early return for uplifted lints: dropping_references, dropping_copy_types, forget_ref, forgetting_copy_types + // early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return, sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 4d1a462d110ca..b0db56bb417ea 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -39,7 +39,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::forget_copy", "forgetting_copy_types"), - ("clippy::forget_ref", "forget_ref"), + ("clippy::forget_ref", "forgetting_references"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index a4fb4fbffdfc8..dfe45dec8a745 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -34,7 +34,7 @@ #![allow(dropping_references)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] -#![allow(forget_ref)] +#![allow(forgetting_references)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -83,7 +83,7 @@ #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(forgetting_copy_types)] -#![warn(forget_ref)] +#![warn(forgetting_references)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index bb833c9534688..ce8eca5a3081c 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -34,7 +34,7 @@ #![allow(dropping_references)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] -#![allow(forget_ref)] +#![allow(forgetting_references)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 8bbd046bbf31b..3fca60aa2ebd3 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -222,11 +222,11 @@ error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` -error: lint `clippy::forget_ref` has been renamed to `forget_ref` +error: lint `clippy::forget_ref` has been renamed to `forgetting_references` --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::forget_ref)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_ref` + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> $DIR/rename.rs:87:9 From 191a901a8d29d292aa20b1161e703f76a4690595 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 21 May 2023 15:02:51 +0200 Subject: [PATCH 462/806] consider array initializer for `large_stack_arrays` --- clippy_lints/src/large_stack_arrays.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 32c6312e06946..0a5901bce046e 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -38,7 +38,7 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Repeat(_, _) = expr.kind + if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) From e9a98d925f8fa3a95e62034954d090e7d142022a Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 21 May 2023 15:07:53 +0200 Subject: [PATCH 463/806] add test case for #10741 --- tests/ui/large_stack_arrays.rs | 13 +++++++++++++ tests/ui/large_stack_arrays.stderr | 30 +++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/tests/ui/large_stack_arrays.rs b/tests/ui/large_stack_arrays.rs index 99787ffd3d395..3e9d5e6a4ca32 100644 --- a/tests/ui/large_stack_arrays.rs +++ b/tests/ui/large_stack_arrays.rs @@ -18,6 +18,19 @@ pub static DOESNOTLINT2: [u8; 512_001] = { [x; 512_001] }; +fn issue_10741() { + #[derive(Copy, Clone)] + struct Large([u32; 100_000]); + + fn build() -> Large { + Large([0; 100_000]) + } + + let _x = [build(); 3]; + + let _y = [build(), build(), build()]; +} + fn main() { let bad = ( [0u32; 20_000_000], diff --git a/tests/ui/large_stack_arrays.stderr b/tests/ui/large_stack_arrays.stderr index 24e90094982aa..118d39566abee 100644 --- a/tests/ui/large_stack_arrays.stderr +++ b/tests/ui/large_stack_arrays.stderr @@ -1,14 +1,30 @@ error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:23:9 + --> $DIR/large_stack_arrays.rs:29:14 + | +LL | let _x = [build(); 3]; + | ^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![build(); 3].into_boxed_slice()` + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:31:14 + | +LL | let _y = [build(), build(), build()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:36:9 | LL | [0u32; 20_000_000], | ^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` - = note: `-D clippy::large-stack-arrays` implied by `-D warnings` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:24:9 + --> $DIR/large_stack_arrays.rs:37:9 | LL | [S { data: [0; 32] }; 5000], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +32,7 @@ LL | [S { data: [0; 32] }; 5000], = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:25:9 + --> $DIR/large_stack_arrays.rs:38:9 | LL | [Some(""); 20_000_000], | ^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | [Some(""); 20_000_000], = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:26:9 + --> $DIR/large_stack_arrays.rs:39:9 | LL | [E::T(0); 5000], | ^^^^^^^^^^^^^^^ @@ -32,12 +48,12 @@ LL | [E::T(0); 5000], = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:27:9 + --> $DIR/large_stack_arrays.rs:40:9 | LL | [0u8; usize::MAX], | ^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From 09f624721cf1ea250aff6b76b465dfbc5c64bc56 Mon Sep 17 00:00:00 2001 From: Andreas Backx Date: Sun, 21 May 2023 16:25:47 +0100 Subject: [PATCH 464/806] [editors/code] add markdown syntax highlighting to doc comments --- editors/code/package.json | 10 ++++++ .../rustdoc.markdown.injection.tmGrammar.json | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 editors/code/rustdoc.markdown.injection.tmGrammar.json diff --git a/editors/code/package.json b/editors/code/package.json index b4620243c9fef..96d63b42393aa 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1562,6 +1562,16 @@ "language": "ra_syntax_tree", "scopeName": "source.ra_syntax_tree", "path": "ra_syntax_tree.tmGrammar.json" + }, + { + "scopeName": "rustdoc.markdown.injection", + "path": "rustdoc.markdown.injection.tmGrammar.json", + "injectTo": [ + "source.rust" + ], + "embeddedLanguages": { + "meta.embedded.block.markdown": "text.html.markdown" + } } ], "problemMatchers": [ diff --git a/editors/code/rustdoc.markdown.injection.tmGrammar.json b/editors/code/rustdoc.markdown.injection.tmGrammar.json new file mode 100644 index 0000000000000..3ee49bff31efa --- /dev/null +++ b/editors/code/rustdoc.markdown.injection.tmGrammar.json @@ -0,0 +1,36 @@ +{ + "scopeName": "rustdoc.markdown.injection", + "injectionSelector": "L:source.rust", + "patterns": [ + { + "include": "#doc-comment-line" + }, + { + "include": "#doc-comment-block" + } + ], + "repository": { + "doc-comment-line": { + "name": "comment.line.documentation.rust", + "begin": "^\\s*//(/|!)", + "while": "^\\s*//(/|!)", + "contentName": "meta.embedded.block.markdown", + "patterns": [ + { + "include": "text.html.markdown" + } + ] + }, + "doc-comment-block": { + "name": "comment.block.documentation.rust", + "begin": "/\\*(\\*|!)", + "end": "\\s*\\*/", + "contentName": "meta.embedded.block.markdown", + "patterns": [ + { + "include": "text.html.markdown" + } + ] + } + } +} From 1e73a9eb4be45092fdd5b76cc50db00970c852f7 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 21 May 2023 23:07:30 +0200 Subject: [PATCH 465/806] do not consider `await` in nested `async` blocks --- clippy_lints/src/unused_async.rs | 48 +++++++++++++++++++++++++++++--- tests/ui/unused_async.rs | 20 +++++++++++++ tests/ui/unused_async.stderr | 21 +++++++++++--- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 55651a28be924..c1339d9463726 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -42,6 +42,10 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); struct AsyncFnVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, found_await: bool, + /// Also keep track of `await`s in nested async blocks so we can mention + /// it in a note + found_await_in_async_block: bool, + async_depth: usize, } impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { @@ -49,7 +53,11 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { - self.found_await = true; + if self.async_depth == 1 { + self.found_await = true; + } else { + self.found_await_in_async_block = true; + } } walk_expr(self, ex); } @@ -57,6 +65,20 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } + + fn visit_body(&mut self, b: &'tcx Body<'tcx>) { + let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_))); + + if is_async_block { + self.async_depth += 1; + } + + walk_body(self, b); + + if is_async_block { + self.async_depth -= 1; + } + } } impl<'tcx> LateLintPass<'tcx> for UnusedAsync { @@ -70,9 +92,27 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { def_id: LocalDefId, ) { if !span.from_expansion() && fn_kind.asyncness().is_async() { - let mut visitor = AsyncFnVisitor { cx, found_await: false }; + let mut visitor = AsyncFnVisitor { + cx, + found_await: false, + async_depth: 0, + found_await_in_async_block: false, + }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { + span_lint_and_then( + cx, + UNUSED_ASYNC, + span, + "unused `async` for function with no await statements", + |diag| { + diag.help("consider removing the `async` from this function"); + + if visitor.found_await_in_async_block { + diag.note("`await` used in an async block, which does not require the enclosing function to be `async`"); + } + }, + ); span_lint_and_help( cx, UNUSED_ASYNC, diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 4ca7f29b34cd0..bfaa5dadfa59e 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -3,6 +3,26 @@ use std::future::Future; use std::pin::Pin; +mod issue10800 { + #![allow(dead_code, unused_must_use, clippy::no_effect)] + + use std::future::ready; + + async fn async_block_await() { + async { + ready(()).await; + }; + } + + async fn normal_block_await() { + { + { + ready(()).await; + } + } + } +} + async fn foo() -> i32 { 4 } diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr index cff3eccbd32bb..e39f9b20b8816 100644 --- a/tests/ui/unused_async.stderr +++ b/tests/ui/unused_async.stderr @@ -1,5 +1,19 @@ error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:6:1 + --> $DIR/unused_async.rs:11:5 + | +LL | / async fn async_block_await() { +LL | | async { +LL | | ready(()).await; +LL | | }; +LL | | } + | |_____^ + | + = help: consider removing the `async` from this function + = note: `await` used in an async block, which does not require the enclosing function to be `async` + = note: `-D clippy::unused-async` implied by `-D warnings` + +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:26:1 | LL | / async fn foo() -> i32 { LL | | 4 @@ -7,10 +21,9 @@ LL | | } | |_^ | = help: consider removing the `async` from this function - = note: `-D clippy::unused-async` implied by `-D warnings` error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:17:5 + --> $DIR/unused_async.rs:37:5 | LL | / async fn unused(&self) -> i32 { LL | | 1 @@ -19,5 +32,5 @@ LL | | } | = help: consider removing the `async` from this function -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From 3eeeaa2bc7bff6b9f11dfe99889a4627904de221 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 22 May 2023 00:01:40 +0200 Subject: [PATCH 466/806] remove old span_lint --- clippy_lints/src/unused_async.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index c1339d9463726..4c68cda7aa714 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; @@ -113,14 +113,6 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } }, ); - span_lint_and_help( - cx, - UNUSED_ASYNC, - span, - "unused `async` for function with no await statements", - None, - "consider removing the `async` from this function", - ); } } } From 1c277d1be896ad5499a464d92a3d427488df470e Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 21 May 2023 23:33:42 -0400 Subject: [PATCH 467/806] Unit tests highlighting unsafe match issue These unit tests generate non-compilable code. I did NOT `bless` them on purpose because the stderr output is not good. I'm surprised we don't auto-compile the suggestions here - is this something that can be easily enabled? See #10808 --- tests/ui/single_match.rs | 23 +++++++++- tests/ui/single_match_else.rs | 86 ++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index d0c9b7b5663e7..d474088aab668 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,5 +1,5 @@ #![warn(clippy::single_match)] -#![allow(clippy::uninlined_format_args)] +#![allow(unused, clippy::uninlined_format_args)] fn dummy() {} @@ -244,3 +244,24 @@ fn main() { _ => 0, }; } + +fn issue_10808(bar: Option) { + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + _ => {}, + } + + match bar { + Some(v) => { + // this comment prevents rustfmt from collapsing the block + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + _ => {}, + } +} diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index c8ac768b60f68..768316edad539 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,6 +1,6 @@ //@aux-build: proc_macros.rs #![warn(clippy::single_match_else)] -#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] +#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macros; use proc_macros::with_span; @@ -115,3 +115,87 @@ fn main() { } } } + +fn issue_10808(bar: Option) { + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + None => { + println!("None1"); + println!("None2"); + }, + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + None => unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + }, + } + + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + None => unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + }, + } + + match bar { + Some(v) => { + // this comment prevents rustfmt from collapsing the block + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + None => { + println!("None"); + println!("None"); + }, + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + None => { + // this comment prevents rustfmt from collapsing the block + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } + + match bar { + Some(v) => { + // this comment prevents rustfmt from collapsing the block + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + None => { + // this comment prevents rustfmt from collapsing the block + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } +} From e92614818879f24994c3890dcb958b8baa2d0678 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 22 May 2023 04:07:17 -0400 Subject: [PATCH 468/806] Fix unsafe blocks --- clippy_utils/src/source.rs | 14 +++-- tests/ui/single_match.stderr | 45 +++++++++++++- tests/ui/single_match_else.stderr | 99 ++++++++++++++++++++++++++++++- 3 files changed, 152 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 0f60290644a18..61e249b995e6b 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -4,7 +4,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{LateContext, LintContext}; use rustc_session::Session; use rustc_span::source_map::{original_sp, SourceMap}; @@ -71,11 +71,17 @@ pub fn expr_block( app: &mut Applicability, ) -> String { let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); - if from_macro { - format!("{{ {code} }}") - } else if let ExprKind::Block(_, _) = expr.kind { + if !from_macro && + let ExprKind::Block(block, _) = expr.kind && + // TODO: Is this enough UnsafeSource::UserProvided, or should CompilerGenerated be also included? + block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + { format!("{code}") } else { + // FIXME: add extra indent for the unsafe blocks: + // original code: unsafe { ... } + // result code: { unsafe { ... } } + // desired code: {\n unsafe { ... }\n} format!("{{ {code} }}") } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 7cecc1b739506..810f153fe621c 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -155,5 +155,48 @@ LL | | (..) => {}, LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` -error: aborting due to 16 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:249:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +LL | | }, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:257:5 + | +LL | / match bar { +LL | | Some(v) => { +LL | | // this comment prevents rustfmt from collapsing the block +LL | | unsafe { +... | +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + // this comment prevents rustfmt from collapsing the block +LL + unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } +LL + } + | + +error: aborting due to 18 previous errors diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 62876a55dc616..6537156d51585 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -100,5 +100,102 @@ LL + return; LL + } | -error: aborting due to 5 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:120:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } else { +LL + println!("None1"); +LL + println!("None2"); +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:131:5 + | +LL | / match bar { +LL | | Some(v) => { +LL | | println!("Some"); +LL | | println!("{v}"); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + println!("Some"); +LL + println!("{v}"); +LL + } else { unsafe { +LL + let v = 0; +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:143:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } else { unsafe { +LL + let v = 0; +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:155:5 + | +LL | / match bar { +LL | | Some(v) => { +LL | | // this comment prevents rustfmt from collapsing the block +LL | | unsafe { +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + // this comment prevents rustfmt from collapsing the block +LL + unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } +LL + } else { +LL + println!("None"); +LL + println!("None"); +LL + } + | + +error: aborting due to 9 previous errors From e6646eb5fd180d94606f7e5a31f1168db6511dc3 Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Mon, 22 May 2023 09:38:16 +0200 Subject: [PATCH 469/806] needless_else: new lint to check for empty else clauses --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/needless_else.rs | 57 ++++++++++ tests/ui-toml/ifs_same_cond/ifs_same_cond.rs | 2 +- .../branches_sharing_code/valid_if_blocks.rs | 6 +- .../valid_if_blocks.stderr | 20 ++-- tests/ui/crashes/ice-7410.rs | 1 + tests/ui/ifs_same_cond.rs | 2 +- tests/ui/needless_else.fixed | 40 +++++++ tests/ui/needless_else.rs | 41 +++++++ tests/ui/needless_else.stderr | 12 ++ tests/ui/needless_return.fixed | 3 +- tests/ui/needless_return.rs | 3 +- tests/ui/needless_return.stderr | 104 +++++++++--------- ...edundant_pattern_matching_drop_order.fixed | 2 +- .../redundant_pattern_matching_drop_order.rs | 2 +- tests/ui/suspicious_else_formatting.rs | 2 +- 18 files changed, 231 insertions(+), 70 deletions(-) create mode 100644 clippy_lints/src/needless_else.rs create mode 100644 tests/ui/needless_else.fixed create mode 100644 tests/ui/needless_else.rs create mode 100644 tests/ui/needless_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f2a47110b9e..3dfc4f7b6a47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4874,6 +4874,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 423eee47742e0..c225e376d148d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -449,6 +449,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b442a4ac5f611..10404d51ba556 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,6 +218,7 @@ mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrowed_ref; mod needless_continue; +mod needless_else; mod needless_for_each; mod needless_late_init; mod needless_parens_on_range_literals; @@ -992,6 +993,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); + store.register_early_pass(|| Box::new(needless_else::NeedlessElse)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs new file mode 100644 index 0000000000000..8faf033e40689 --- /dev/null +++ b/clippy_lints/src/needless_else.rs @@ -0,0 +1,57 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span, span_extract_comment}; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `else` branches. + /// + /// ### Why is this bad? + /// An empty else branch does nothing and can be removed. + /// + /// ### Example + /// ```rust + ///# fn check() -> bool { true } + /// if check() { + /// println!("Check successful!"); + /// } else { + /// } + /// ``` + /// Use instead: + /// ```rust + ///# fn check() -> bool { true } + /// if check() { + /// println!("Check successful!"); + /// } + /// ``` + #[clippy::version = "1.71.0"] + pub NEEDLESS_ELSE, + style, + "empty else branch" +} +declare_lint_pass!(NeedlessElse => [NEEDLESS_ELSE]); + +impl EarlyLintPass for NeedlessElse { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind && + let ExprKind::Block(block, _) = &else_clause.kind && + !expr.span.from_expansion() && + !else_clause.span.from_expansion() && + block.stmts.is_empty() { + let span = trim_span(cx.sess().source_map(), expr.span.trim_start(then_block.span).unwrap()); + if span_extract_comment(cx.sess().source_map(), span).is_empty() { + span_lint_and_sugg( + cx, + NEEDLESS_ELSE, + span, + "this else branch is empty", + "you can remove it", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index d623ac7e02008..4882416c414ac 100644 --- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] +#![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)] fn main() {} diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index 2d6055eb6c422..5780ea0893777 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,6 +1,10 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] +#![allow( + clippy::mixed_read_write_in_expression, + clippy::uninlined_format_args, + clippy::needless_else +)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr index ce7fff0122f1f..a7e72b780affc 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:105:14 + --> $DIR/valid_if_blocks.rs:109:14 | LL | if false { | ______________^ @@ -7,7 +7,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:106:12 + --> $DIR/valid_if_blocks.rs:110:12 | LL | } else { | ____________^ @@ -20,7 +20,7 @@ LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:116:15 + --> $DIR/valid_if_blocks.rs:120:15 | LL | if x == 0 { | _______________^ @@ -31,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:120:12 + --> $DIR/valid_if_blocks.rs:124:12 | LL | } else { | ____________^ @@ -42,19 +42,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:127:23 + --> $DIR/valid_if_blocks.rs:131:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:127:34 + --> $DIR/valid_if_blocks.rs:131:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:133:23 + --> $DIR/valid_if_blocks.rs:137:23 | LL | } else if x == 68 { | _______________________^ @@ -66,7 +66,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:138:12 + --> $DIR/valid_if_blocks.rs:142:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:147:23 + --> $DIR/valid_if_blocks.rs:151:23 | LL | } else if x == 68 { | _______________________^ @@ -88,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:150:12 + --> $DIR/valid_if_blocks.rs:154:12 | LL | } else { | ____________^ diff --git a/tests/ui/crashes/ice-7410.rs b/tests/ui/crashes/ice-7410.rs index ffe20ab1c3101..a5373cdcae120 100644 --- a/tests/ui/crashes/ice-7410.rs +++ b/tests/ui/crashes/ice-7410.rs @@ -6,6 +6,7 @@ #![no_std] #![allow(clippy::if_same_then_else)] #![allow(clippy::redundant_pattern_matching)] +#![allow(clippy::needless_else)] use core::panic::PanicInfo; diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 9ce9a87626a79..f62da157d1b94 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/tests/ui/needless_else.fixed b/tests/ui/needless_else.fixed new file mode 100644 index 0000000000000..14f81728a868a --- /dev/null +++ b/tests/ui/needless_else.fixed @@ -0,0 +1,40 @@ +//@run-rustfix +#![allow(unused)] +#![warn(clippy::needless_else)] +#![allow(clippy::suspicious_else_formatting)] + +macro_rules! mac { + ($test:expr) => { + if $test { + println!("Test successful!"); + } else { + } + }; +} + +fn main() { + let b = std::hint::black_box(true); + + if b { + println!("Foobar"); + } + + if b { + println!("Foobar"); + } else { + // Do not lint because this comment might be important + } + + if b { + println!("Foobar"); + } else + /* Do not lint because this comment might be important */ + { + } + + // Do not lint because of the expression + let _ = if b { 1 } else { 2 }; + + // Do not lint because inside a macro + mac!(b); +} diff --git a/tests/ui/needless_else.rs b/tests/ui/needless_else.rs new file mode 100644 index 0000000000000..fae11818141b7 --- /dev/null +++ b/tests/ui/needless_else.rs @@ -0,0 +1,41 @@ +//@run-rustfix +#![allow(unused)] +#![warn(clippy::needless_else)] +#![allow(clippy::suspicious_else_formatting)] + +macro_rules! mac { + ($test:expr) => { + if $test { + println!("Test successful!"); + } else { + } + }; +} + +fn main() { + let b = std::hint::black_box(true); + + if b { + println!("Foobar"); + } else { + } + + if b { + println!("Foobar"); + } else { + // Do not lint because this comment might be important + } + + if b { + println!("Foobar"); + } else + /* Do not lint because this comment might be important */ + { + } + + // Do not lint because of the expression + let _ = if b { 1 } else { 2 }; + + // Do not lint because inside a macro + mac!(b); +} diff --git a/tests/ui/needless_else.stderr b/tests/ui/needless_else.stderr new file mode 100644 index 0000000000000..a7b2f1959c77b --- /dev/null +++ b/tests/ui/needless_else.stderr @@ -0,0 +1,12 @@ +error: this else branch is empty + --> $DIR/needless_else.rs:20:7 + | +LL | } else { + | _______^ +LL | | } + | |_____^ help: you can remove it + | + = note: `-D clippy::needless-else` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d49ae5d8636f5..4dabf313963de 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -7,7 +7,8 @@ clippy::if_same_then_else, clippy::single_match, clippy::needless_bool, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::needless_else )] #![warn(clippy::needless_return)] diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 367638261746c..542f562b31496 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -7,7 +7,8 @@ clippy::if_same_then_else, clippy::single_match, clippy::needless_bool, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::needless_else )] #![warn(clippy::needless_return)] diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 05f6038cd2553..b5a7d61139d8d 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:27:5 + --> $DIR/needless_return.rs:28:5 | LL | return true; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:31:5 + --> $DIR/needless_return.rs:32:5 | LL | return true; | ^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:36:5 + --> $DIR/needless_return.rs:37:5 | LL | return true;;; | ^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | return true;;; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:41:5 + --> $DIR/needless_return.rs:42:5 | LL | return true;; ; ; | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | return true;; ; ; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:9 + --> $DIR/needless_return.rs:47:9 | LL | return true; | ^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:48:9 + --> $DIR/needless_return.rs:49:9 | LL | return false; | ^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:17 + --> $DIR/needless_return.rs:55:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:56:13 + --> $DIR/needless_return.rs:57:13 | LL | return true; | ^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:63:9 + --> $DIR/needless_return.rs:64:9 | LL | return true; | ^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:65:16 + --> $DIR/needless_return.rs:66:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:69:5 + --> $DIR/needless_return.rs:70:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:72:21 + --> $DIR/needless_return.rs:73:21 | LL | fn test_void_fun() { | _____________________^ @@ -98,7 +98,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:77:11 + --> $DIR/needless_return.rs:78:11 | LL | if b { | ___________^ @@ -108,7 +108,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:79:13 + --> $DIR/needless_return.rs:80:13 | LL | } else { | _____________^ @@ -118,7 +118,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:88:14 | LL | _ => return, | ^^^^^^ @@ -126,7 +126,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:95:24 + --> $DIR/needless_return.rs:96:24 | LL | let _ = 42; | ________________________^ @@ -136,7 +136,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:98:14 + --> $DIR/needless_return.rs:99:14 | LL | _ => return, | ^^^^^^ @@ -144,7 +144,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:111:9 + --> $DIR/needless_return.rs:112:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,7 +152,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:9 + --> $DIR/needless_return.rs:114:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -160,7 +160,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:135:32 + --> $DIR/needless_return.rs:136:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -168,7 +168,7 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:139:21 + --> $DIR/needless_return.rs:140:21 | LL | let _ = || { | _____________________^ @@ -178,7 +178,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:142:20 + --> $DIR/needless_return.rs:143:20 | LL | let _ = || return; | ^^^^^^ @@ -186,7 +186,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:148:32 + --> $DIR/needless_return.rs:149:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -194,7 +194,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:157:5 + --> $DIR/needless_return.rs:158:5 | LL | return true; | ^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:161:5 + --> $DIR/needless_return.rs:162:5 | LL | return true; | ^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:166:9 + --> $DIR/needless_return.rs:167:9 | LL | return true; | ^^^^^^^^^^^ @@ -218,7 +218,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:168:9 + --> $DIR/needless_return.rs:169:9 | LL | return false; | ^^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:174:17 + --> $DIR/needless_return.rs:175:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -234,7 +234,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:176:13 + --> $DIR/needless_return.rs:177:13 | LL | return true; | ^^^^^^^^^^^ @@ -242,7 +242,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:183:9 + --> $DIR/needless_return.rs:184:9 | LL | return true; | ^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:185:16 + --> $DIR/needless_return.rs:186:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -258,7 +258,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:189:5 + --> $DIR/needless_return.rs:190:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -266,7 +266,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:192:33 + --> $DIR/needless_return.rs:193:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -276,7 +276,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:197:11 + --> $DIR/needless_return.rs:198:11 | LL | if b { | ___________^ @@ -286,7 +286,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:199:13 + --> $DIR/needless_return.rs:200:13 | LL | } else { | _____________^ @@ -296,7 +296,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:207:14 + --> $DIR/needless_return.rs:208:14 | LL | _ => return, | ^^^^^^ @@ -304,7 +304,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:220:9 + --> $DIR/needless_return.rs:221:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:222:9 + --> $DIR/needless_return.rs:223:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:238:5 + --> $DIR/needless_return.rs:239:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:250:9 + --> $DIR/needless_return.rs:251:9 | LL | return true; | ^^^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:252:9 + --> $DIR/needless_return.rs:253:9 | LL | return false; | ^^^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:259:13 + --> $DIR/needless_return.rs:260:13 | LL | return 10; | ^^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:262:13 + --> $DIR/needless_return.rs:263:13 | LL | return 100; | ^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:270:9 + --> $DIR/needless_return.rs:271:9 | LL | return 0; | ^^^^^^^^ @@ -368,7 +368,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:277:13 + --> $DIR/needless_return.rs:278:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:279:13 + --> $DIR/needless_return.rs:280:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +384,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:286:20 + --> $DIR/needless_return.rs:287:20 | LL | let _ = 42; | ____________________^ @@ -395,7 +395,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:293:20 + --> $DIR/needless_return.rs:294:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -403,7 +403,7 @@ LL | let _ = 42; return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:305:9 + --> $DIR/needless_return.rs:306:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -411,7 +411,7 @@ LL | return Ok(format!("ok!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:307:9 + --> $DIR/needless_return.rs:308:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL | return Err(format!("err!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:313:9 + --> $DIR/needless_return.rs:314:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | return if true { 1 } else { 2 }; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:317:9 + --> $DIR/needless_return.rs:318:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed index bebdf89716fa2..481c9b263fbb8 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.fixed +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs index 8fb6ed5f7eca0..86e46d41e65a1 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.rs +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index e0153cdd8cdcf..4823d9092089b 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else, clippy::let_unit_value)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value, clippy::needless_else)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; From 3e1302fa0c50cc41535b6e23d9cf7e686f4a4fdd Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 22 May 2023 14:04:13 +0200 Subject: [PATCH 470/806] [`match_wild_err_arm`]: do not lint in const contexts --- clippy_lints/src/matches/match_wild_err_arm.rs | 7 ++++++- tests/ui/match_wild_err_arm.rs | 15 ++++++++++++++- tests/ui/match_wild_err_arm.stderr | 8 ++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index 42f1e2629d41a..6424ac31d9f68 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_wild, peel_blocks_with_stmt}; +use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; @@ -10,6 +10,11 @@ use rustc_span::symbol::{kw, sym}; use super::MATCH_WILD_ERR_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) { + // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now + if in_constant(cx, ex.hir_id) { + return; + } + let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym::Result) { for arm in arms { diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index 823be65efe065..5a552e4ae51a5 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,7 +1,20 @@ #![feature(exclusive_range_pattern)] -#![allow(clippy::match_same_arms)] +#![allow(clippy::match_same_arms, dead_code)] #![warn(clippy::match_wild_err_arm)] +fn issue_10635() { + enum Error { + A, + B, + } + + // Don't trigger in const contexts. Const unwrap is not yet stable + const X: () = match Ok::<_, Error>(()) { + Ok(x) => x, + Err(_) => panic!(), + }; +} + fn match_wild_err_arm() { let x: Result = Ok(3); diff --git a/tests/ui/match_wild_err_arm.stderr b/tests/ui/match_wild_err_arm.stderr index b016d682698c8..a9f54feacdb58 100644 --- a/tests/ui/match_wild_err_arm.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:11:9 + --> $DIR/match_wild_err_arm.rs:24:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"), = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:17:9 + --> $DIR/match_wild_err_arm.rs:30:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -16,7 +16,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:23:9 + --> $DIR/match_wild_err_arm.rs:36:9 | LL | Err(_) => { | ^^^^^^ @@ -24,7 +24,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:31:9 + --> $DIR/match_wild_err_arm.rs:44:9 | LL | Err(_e) => panic!(), | ^^^^^^^ From 8c82486ea92219d1c0c720fc598abac27c81a966 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 22 May 2023 16:13:23 +0200 Subject: [PATCH 471/806] [`default_constructed_unit_structs`]: do not lint type aliases --- .../src/default_constructed_unit_structs.rs | 15 +++++++++++-- clippy_utils/src/lib.rs | 2 +- .../ui/default_constructed_unit_structs.fixed | 22 +++++++++++++++++++ tests/ui/default_constructed_unit_structs.rs | 22 +++++++++++++++++++ .../default_constructed_unit_structs.stderr | 8 +++---- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index 9bd7a0dc0f3b8..fb037bbcbf3eb 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, match_def_path, paths}; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_ty_alias, match_def_path, paths}; use hir::{def::Res, ExprKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -43,12 +43,23 @@ declare_clippy_lint! { } declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]); +fn is_alias(ty: hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(ref qpath) = ty.kind { + is_ty_alias(qpath) + } else { + false + } +} + impl LateLintPass<'_> for DefaultConstructedUnitStructs { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if_chain!( // make sure we have a call to `Default::default` if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; - if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind; + if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind; + // make sure this isn't a type alias: + // `::Assoc` cannot be used as a constructor + if !is_alias(*base); if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); // make sure we have a struct with no fields (unit struct) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 575c29a6b6f97..8c883445a7984 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -287,7 +287,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { - QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias, ..)), + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)), QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, _ => false, } diff --git a/tests/ui/default_constructed_unit_structs.fixed b/tests/ui/default_constructed_unit_structs.fixed index e1012f38bba2a..ac5fe38ff4439 100644 --- a/tests/ui/default_constructed_unit_structs.fixed +++ b/tests/ui/default_constructed_unit_structs.fixed @@ -101,6 +101,28 @@ struct EmptyStruct {} #[non_exhaustive] struct NonExhaustiveStruct; +mod issue_10755 { + struct Sqlite {} + + trait HasArguments<'q> { + type Arguments; + } + + impl<'q> HasArguments<'q> for Sqlite { + type Arguments = std::marker::PhantomData<&'q ()>; + } + + type SqliteArguments<'q> = >::Arguments; + + fn foo() { + // should not lint + // type alias cannot be used as a constructor + let _ = ::Arguments::default(); + + let _ = SqliteArguments::default(); + } +} + fn main() { // should lint let _ = PhantomData::; diff --git a/tests/ui/default_constructed_unit_structs.rs b/tests/ui/default_constructed_unit_structs.rs index c7b4313dbf0c9..de7f14ffbd95c 100644 --- a/tests/ui/default_constructed_unit_structs.rs +++ b/tests/ui/default_constructed_unit_structs.rs @@ -101,6 +101,28 @@ struct EmptyStruct {} #[non_exhaustive] struct NonExhaustiveStruct; +mod issue_10755 { + struct Sqlite {} + + trait HasArguments<'q> { + type Arguments; + } + + impl<'q> HasArguments<'q> for Sqlite { + type Arguments = std::marker::PhantomData<&'q ()>; + } + + type SqliteArguments<'q> = >::Arguments; + + fn foo() { + // should not lint + // type alias cannot be used as a constructor + let _ = ::Arguments::default(); + + let _ = SqliteArguments::default(); + } +} + fn main() { // should lint let _ = PhantomData::::default(); diff --git a/tests/ui/default_constructed_unit_structs.stderr b/tests/ui/default_constructed_unit_structs.stderr index 61a32fb10e53b..13abb9149da24 100644 --- a/tests/ui/default_constructed_unit_structs.stderr +++ b/tests/ui/default_constructed_unit_structs.stderr @@ -13,25 +13,25 @@ LL | inner: PhantomData::default(), | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:106:33 + --> $DIR/default_constructed_unit_structs.rs:128:33 | LL | let _ = PhantomData::::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:107:42 + --> $DIR/default_constructed_unit_structs.rs:129:42 | LL | let _: PhantomData = PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:108:55 + --> $DIR/default_constructed_unit_structs.rs:130:55 | LL | let _: PhantomData = std::marker::PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:109:23 + --> $DIR/default_constructed_unit_structs.rs:131:23 | LL | let _ = UnitStruct::default(); | ^^^^^^^^^^^ help: remove this call to `default` From 01f42d240570fd933fb6407a7b010dbab53563ff Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 21 May 2023 21:25:01 +0900 Subject: [PATCH 472/806] fix: introduce new type var when expectation for ref pat is not ref --- crates/hir-ty/src/infer/pat.rs | 13 ++++++++++--- crates/hir-ty/src/tests/patterns.rs | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 05f6fcaead26f..0c2b179a10d69 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -313,16 +313,23 @@ impl<'a> InferenceContext<'a> { fn infer_ref_pat( &mut self, - pat: PatId, + inner_pat: PatId, mutability: Mutability, expected: &Ty, default_bm: BindingMode, ) -> Ty { let expectation = match expected.as_reference() { Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(), - _ => self.result.standard_types.unknown.clone(), + None => { + let inner_ty = self.table.new_type_var(); + let ref_ty = + TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner); + // Unification failure will be reported by the caller. + self.unify(&ref_ty, expected); + inner_ty + } }; - let subty = self.infer_pat(pat, &expectation, default_bm); + let subty = self.infer_pat(inner_pat, &expectation, default_bm); TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner) } diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index c8c31bdea5cbf..b73f0d72a3f91 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1,6 +1,6 @@ use expect_test::expect; -use super::{check, check_infer, check_infer_with_mismatches, check_types}; +use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; #[test] fn infer_pattern() { @@ -240,6 +240,21 @@ fn infer_pattern_match_ergonomics_ref() { ); } +#[test] +fn ref_pat_with_inference_variable() { + check_no_mismatches( + r#" +enum E { A } +fn test() { + let f = |e| match e { + &E::A => {} + }; + f(&E::A); +} +"#, + ); +} + #[test] fn infer_pattern_match_slice() { check_infer( From 9fd34e0c7522fd52bf7d015de4ed78c24596498b Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 22 May 2023 20:02:45 -0400 Subject: [PATCH 473/806] Use #[rustfmt::skip] --- tests/ui/single_match.rs | 2 +- tests/ui/single_match.stderr | 3 +-- tests/ui/single_match_else.rs | 8 ++++---- tests/ui/single_match_else.stderr | 3 +-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index d474088aab668..3455ca5537af0 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -255,8 +255,8 @@ fn issue_10808(bar: Option) { } match bar { + #[rustfmt::skip] Some(v) => { - // this comment prevents rustfmt from collapsing the block unsafe { let r = &v as *const i32; println!("{}", *r); diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 810f153fe621c..dad66e2ab2ec3 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -179,8 +179,8 @@ error: you seem to be trying to use `match` for destructuring a single pattern. --> $DIR/single_match.rs:257:5 | LL | / match bar { +LL | | #[rustfmt::skip] LL | | Some(v) => { -LL | | // this comment prevents rustfmt from collapsing the block LL | | unsafe { ... | LL | | _ => {}, @@ -190,7 +190,6 @@ LL | | } help: try this | LL ~ if let Some(v) = bar { -LL + // this comment prevents rustfmt from collapsing the block LL + unsafe { LL + let r = &v as *const i32; LL + println!("{}", *r); diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 768316edad539..ec97bfc845f8f 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -153,8 +153,8 @@ fn issue_10808(bar: Option) { } match bar { + #[rustfmt::skip] Some(v) => { - // this comment prevents rustfmt from collapsing the block unsafe { let r = &v as *const i32; println!("{}", *r); @@ -171,8 +171,8 @@ fn issue_10808(bar: Option) { println!("Some"); println!("{v}"); }, + #[rustfmt::skip] None => { - // this comment prevents rustfmt from collapsing the block unsafe { let v = 0; let r = &v as *const i32; @@ -182,15 +182,15 @@ fn issue_10808(bar: Option) { } match bar { + #[rustfmt::skip] Some(v) => { - // this comment prevents rustfmt from collapsing the block unsafe { let r = &v as *const i32; println!("{}", *r); } }, + #[rustfmt::skip] None => { - // this comment prevents rustfmt from collapsing the block unsafe { let v = 0; let r = &v as *const i32; diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 6537156d51585..228236f3bb8e8 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -175,8 +175,8 @@ error: you seem to be trying to use `match` for destructuring a single pattern. --> $DIR/single_match_else.rs:155:5 | LL | / match bar { +LL | | #[rustfmt::skip] LL | | Some(v) => { -LL | | // this comment prevents rustfmt from collapsing the block LL | | unsafe { ... | LL | | }, @@ -186,7 +186,6 @@ LL | | } help: try this | LL ~ if let Some(v) = bar { -LL + // this comment prevents rustfmt from collapsing the block LL + unsafe { LL + let r = &v as *const i32; LL + println!("{}", *r); From 68df61ebd9e0e6b75299185e545bd608369c4883 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 22 May 2023 20:06:58 -0400 Subject: [PATCH 474/806] remove todo --- clippy_utils/src/source.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 61e249b995e6b..582337b47e813 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -73,7 +73,6 @@ pub fn expr_block( let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, _) = expr.kind && - // TODO: Is this enough UnsafeSource::UserProvided, or should CompilerGenerated be also included? block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { format!("{code}") From 51ec2ced69901f34b3097a6af12b7dd102ed504c Mon Sep 17 00:00:00 2001 From: alibektas Date: Mon, 17 Apr 2023 00:41:08 +0300 Subject: [PATCH 475/806] Improve ast::make Add `ty_alias` and make `impl_trait` , `fn` and `impl_` have more coverage. --- .../src/handlers/generate_delegate_methods.rs | 31 ++++- .../src/handlers/generate_function.rs | 2 + crates/syntax/src/ast/make.rs | 127 ++++++++++++++---- 3 files changed, 126 insertions(+), 34 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index a0813c91154f6..23e6adcf53a02 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -116,12 +116,25 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ); let ret_type = method_source.ret_type(); let is_async = method_source.async_token().is_some(); + let is_const = method_source.const_token().is_some(); + let is_unsafe = method_source.unsafe_token().is_some(); let tail_expr_finished = if is_async { make::expr_await(tail_expr) } else { tail_expr }; let body = make::block_expr([], Some(tail_expr_finished)); - let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async) - .indent(ast::edit::IndentLevel(1)) - .clone_for_update(); + let f = make::fn_( + vis, + name, + type_params, + None, + params, + body, + ret_type, + is_async, + is_const, + is_unsafe, + ) + .indent(ast::edit::IndentLevel(1)) + .clone_for_update(); let cursor = Cursor::Before(f.syntax()); @@ -153,8 +166,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); let ty_params = params.clone(); - let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params) - .clone_for_update(); + let where_clause = strukt.where_clause(); + + let impl_def = make::impl_( + ty_params, + None, + make::ty_path(make::ext::ident_path(name)), + where_clause, + None, + ) + .clone_for_update(); let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 739b466b311a4..68b61fc03ffac 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -378,6 +378,8 @@ impl FunctionBuilder { fn_body, self.ret_type, self.is_async, + false, // FIXME : const and unsafe are not handled yet. + false, ); let leading_ws; let trailing_ws; diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index cc0d7f4b48e33..3472a42458754 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -158,13 +158,6 @@ fn ty_from_text(text: &str) -> ast::Type { ast_from_text(&format!("type _T = {text};")) } -/// Related goto [link](https://doc.rust-lang.org/reference/items/type-aliases.html) -/// Type Alias syntax is -/// ``` -/// TypeAlias : -/// type IDENTIFIER GenericParams? ( : TypeParamBounds )? WhereClause? ( = Type WhereClause?)? ; -/// ``` -/// FIXME : ident should be of type ast::Ident pub fn ty_alias( ident: &str, generic_param_list: Option, @@ -173,7 +166,7 @@ pub fn ty_alias( assignment: Option<(ast::Type, Option)>, ) -> ast::TypeAlias { let mut s = String::new(); - s.push_str(&format!("type {}", ident)); + s.push_str(&format!("type {} ", ident)); if let Some(list) = generic_param_list { s.push_str(&list.to_string()); @@ -203,33 +196,106 @@ pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } -// FIXME: `ty_params` should be `ast::GenericArgList` +fn merge_gen_params( + ps: Option, + bs: Option, +) -> Option { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + for b in bs.generic_params() { + ps.add_generic_param(b); + } + Some(ps) + } + } +} + pub fn impl_( - ty: ast::Path, - params: Option, - ty_params: Option, + generic_params: Option, + generic_args: Option, + path_type: ast::Type, + where_clause: Option, + body: Option>>, ) -> ast::Impl { - let params = match params { - Some(params) => params.to_string(), - None => String::new(), + let (gen_params, tr_gen_args) = match (generic_params, generic_args) { + (None, None) => (String::new(), String::new()), + (None, Some(args)) => (String::new(), args.to_generic_args().to_string()), + (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()), + (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) { + Some(merged) => (params.to_string(), merged.to_generic_args().to_string()), + None => (params.to_string(), String::new()), + }, }; - let ty_params = match ty_params { - Some(params) => params.to_string(), + + let where_clause = match where_clause { + Some(pr) => pr.to_string(), + None => " ".to_string(), + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), None => String::new(), }; - ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}")) + + ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body)) } +// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs` +// `add_generic_arg()` just like `add_generic_param()` +// is implemented for `ast::GenericParamList` pub fn impl_trait( - trait_: ast::Path, - ty: ast::Path, - ty_params: Option, + is_unsafe: bool, + trait_gen_params: Option, + trait_gen_args: Option, + type_gen_params: Option, + type_gen_args: Option, + is_negative: bool, + path_type: ast::Type, + ty: ast::Type, + trait_where_clause: Option, + ty_where_clause: Option, + body: Option>>, ) -> ast::Impl { - // TODO : If this function is now correct we can also change `impl_` accordingly` - let ty_params_str = ty_params.as_ref().map_or_else(String::new, |params| params.to_string()); - let ty_genargs_str = - ty_params.map_or_else(String::new, |params| params.to_generic_args().to_string()); - ast_from_text(&format!("impl{ty_params_str} {trait_} for {ty}{ty_genargs_str} {{}}")) + let is_unsafe = if is_unsafe { "unsafe " } else { "" }; + let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { + Some(pars) => pars.to_string(), + None => String::new(), + }; + + let is_negative = if is_negative { "! " } else { "" }; + + let where_clause = match (ty_where_clause, trait_where_clause) { + (None, None) => " ".to_string(), + (None, Some(tr)) => format!("\n{}\n", tr).to_string(), + (Some(ty), None) => format!("\n{}\n", ty).to_string(), + (Some(ty), Some(tr)) => { + let updated = ty.clone_for_update(); + tr.predicates().for_each(|p| { + ty.add_predicate(p); + }); + format!("\n{}\n", updated).to_string() + } + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) } pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { @@ -871,6 +937,8 @@ pub fn fn_( body: ast::BlockExpr, ret_type: Option, is_async: bool, + is_const: bool, + is_unsafe: bool, ) -> ast::Fn { let type_params = match type_params { Some(type_params) => format!("{type_params}"), @@ -890,12 +958,13 @@ pub fn fn_( }; let async_literal = if is_async { "async " } else { "" }; + let const_literal = if is_const { "const " } else { "" }; + let unsafe_literal = if is_unsafe { "unsafe " } else { "" }; ast_from_text(&format!( - "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } - pub fn struct_( visibility: Option, strukt_name: ast::Name, @@ -945,7 +1014,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n", + "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) }); From ed935de08761ef3d463b7770f9c5d8b7422bed82 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 22 May 2023 23:00:28 -0400 Subject: [PATCH 476/806] Run-rustfix --- tests/ui/single_match.fixed | 209 +++++++++++++++++++++++++++++++ tests/ui/single_match.rs | 4 +- tests/ui/single_match_else.fixed | 173 +++++++++++++++++++++++++ tests/ui/single_match_else.rs | 2 +- 4 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 tests/ui/single_match.fixed create mode 100644 tests/ui/single_match_else.fixed diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed new file mode 100644 index 0000000000000..77a2cf3b991f6 --- /dev/null +++ b/tests/ui/single_match.fixed @@ -0,0 +1,209 @@ +//@run-rustfix +#![warn(clippy::single_match)] +#![allow(unused, clippy::uninlined_format_args, clippy::redundant_pattern_matching)] +fn dummy() {} + +fn single_match() { + let x = Some(1u8); + + if let Some(y) = x { + println!("{:?}", y); + }; + + let x = Some(1u8); + if let Some(y) = x { println!("{:?}", y) } + + let z = (1u8, 1u8); + if let (2..=3, 7..=9) = z { dummy() }; + + // Not linted (pattern guards used) + match x { + Some(y) if y == 0 => println!("{:?}", y), + _ => (), + } + + // Not linted (no block with statements in the single arm) + match z { + (2..=3, 7..=9) => println!("{:?}", z), + _ => println!("nope"), + } +} + +enum Foo { + Bar, + Baz(u8), +} +use std::borrow::Cow; +use Foo::*; + +fn single_match_know_enum() { + let x = Some(1u8); + let y: Result<_, i8> = Ok(1i8); + + if let Some(y) = x { dummy() }; + + if let Ok(y) = y { dummy() }; + + let c = Cow::Borrowed(""); + + if let Cow::Borrowed(..) = c { dummy() }; + + let z = Foo::Bar; + // no warning + match z { + Bar => println!("42"), + Baz(_) => (), + } + + match z { + Baz(_) => println!("42"), + Bar => (), + } +} + +// issue #173 +fn if_suggestion() { + let x = "test"; + if x == "test" { println!() } + + #[derive(PartialEq, Eq)] + enum Foo { + A, + B, + C(u32), + } + + let x = Foo::A; + if x == Foo::A { println!() } + + const FOO_C: Foo = Foo::C(0); + if x == FOO_C { println!() } + + if x == Foo::A { println!() } + + let x = &x; + if x == &Foo::A { println!() } + + enum Bar { + A, + B, + } + impl PartialEq for Bar { + fn eq(&self, rhs: &Self) -> bool { + matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B)) + } + } + impl Eq for Bar {} + + let x = Bar::A; + if let Bar::A = x { println!() } + + // issue #7038 + struct X; + let x = Some(X); + if let None = x { println!() }; +} + +// See: issue #8282 +fn ranges() { + enum E { + V, + } + let x = (Some(E::V), Some(42)); + + // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the + // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared + // because of `match` construction. + match x { + (Some(E::V), _) => {}, + (None, _) => {}, + } + + // lint + if let (Some(_), _) = x {} + + // lint + if let (Some(E::V), _) = x { todo!() } + + // lint + if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {} + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (.., Some(E::V), _) => {}, + (.., None, _) => {}, + } + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (Some(E::V), ..) => {}, + (None, ..) => {}, + } + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (_, Some(E::V), ..) => {}, + (_, None, ..) => {}, + } +} + +fn skip_type_aliases() { + enum OptionEx { + Some(i32), + None, + } + enum ResultEx { + Err(i32), + Ok(i32), + } + + use OptionEx::{None, Some}; + use ResultEx::{Err, Ok}; + + // don't lint + match Err(42) { + Ok(_) => dummy(), + Err(_) => (), + }; + + // don't lint + match Some(1i32) { + Some(_) => dummy(), + None => (), + }; +} + +macro_rules! single_match { + ($num:literal) => { + match $num { + 15 => println!("15"), + _ => (), + } + }; +} + +fn main() { + single_match!(5); + + // Don't lint + let _ = match Some(0) { + #[cfg(feature = "foo")] + Some(10) => 11, + Some(x) => x, + _ => 0, + }; +} + +fn issue_10808(bar: Option) { + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + } +} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 3455ca5537af0..8d0ab5b99ad51 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,6 +1,6 @@ +//@run-rustfix #![warn(clippy::single_match)] -#![allow(unused, clippy::uninlined_format_args)] - +#![allow(unused, clippy::uninlined_format_args, clippy::redundant_pattern_matching)] fn dummy() {} fn single_match() { diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed new file mode 100644 index 0000000000000..f88498655a418 --- /dev/null +++ b/tests/ui/single_match_else.fixed @@ -0,0 +1,173 @@ +//@run-rustfix +//@aux-build: proc_macros.rs +#![warn(clippy::single_match_else)] +#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] +extern crate proc_macros; +use proc_macros::with_span; + +enum ExprNode { + ExprAddrOf, + Butterflies, + Unicorns, +} + +static NODE: ExprNode = ExprNode::Unicorns; + +fn unwrap_addr() -> Option<&'static ExprNode> { + let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { + let x = 5; + None + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) +} + +macro_rules! unwrap_addr { + ($expression:expr) => { + match $expression { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } + }; +} + +#[rustfmt::skip] +fn main() { + unwrap_addr!(ExprNode::Unicorns); + + // + // don't lint single exprs/statements + // + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => return, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return + }, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return; + }, + } + + // + // lint multiple exprs/statements "else" blocks + // + + // lint here + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return + } + + // lint here + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + + // lint here + use std::convert::Infallible; + if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + + use std::borrow::Cow; + if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else { + println!("else block"); + return; + } +} + +fn issue_10808(bar: Option) { + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } else { + println!("None1"); + println!("None2"); + } + + if let Some(v) = bar { + println!("Some"); + println!("{v}"); + } else { unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } else { unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + } else { + println!("None"); + println!("None"); + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } + + match bar { + #[rustfmt::skip] + Some(v) => { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } +} diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index ec97bfc845f8f..b34b95539190c 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,7 +1,7 @@ +//@run-rustfix //@aux-build: proc_macros.rs #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] - extern crate proc_macros; use proc_macros::with_span; From 18fdca37cf0c1e0b8e2a71c9fb36d02467b618ef Mon Sep 17 00:00:00 2001 From: Esme Yi Date: Tue, 23 May 2023 16:23:59 +0800 Subject: [PATCH 477/806] Support rust metadata for AIX. --- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- .../rustc_codegen_ssa/src/back/metadata.rs | 149 ++++++++++++++++-- 2 files changed, 139 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 0ac12d32be535..984efa2104446 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -48,7 +48,7 @@ libc = "0.2.50" [dependencies.object] version = "0.31.1" default-features = false -features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] +features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"] [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 8bf84772f0869..9f0ca3863bb60 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -6,8 +6,8 @@ use std::path::Path; use object::write::{self, StandardSegment, Symbol, SymbolSection}; use object::{ - elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, - SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, + elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, + ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; use snap::write::FrameEncoder; @@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target}; #[derive(Debug)] pub struct DefaultMetadataLoader; +static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata"; + fn load_metadata_with( path: &Path, f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, @@ -48,7 +50,7 @@ fn load_metadata_with( } impl MetadataLoader for DefaultMetadataLoader { - fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result { + fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result { load_metadata_with(path, |data| { let archive = object::read::archive::ArchiveFile::parse(&*data) .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; @@ -60,7 +62,11 @@ impl MetadataLoader for DefaultMetadataLoader { let data = entry .data(data) .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; - return search_for_section(path, data, ".rmeta"); + if target.is_like_aix { + return get_metadata_xcoff(path, data); + } else { + return search_for_section(path, data, ".rmeta"); + } } } @@ -68,8 +74,12 @@ impl MetadataLoader for DefaultMetadataLoader { }) } - fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { - load_metadata_with(path, |data| search_for_section(path, data, ".rustc")) + fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result { + if target.is_like_aix { + load_metadata_with(path, |data| get_metadata_xcoff(path, data)) + } else { + load_metadata_with(path, |data| search_for_section(path, data, ".rustc")) + } } } @@ -141,6 +151,33 @@ fn add_gnu_property_note( file.append_section_data(section, &data, 8); } +pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> { + let Ok(file) = object::File::parse(data) else { + return Ok(data); + }; + let info_data = search_for_section(path, data, ".info")?; + if let Some(metadata_symbol) = + file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME)) + { + let offset = metadata_symbol.address() as usize; + if offset < 4 { + return Err(format!("Invalid metadata symbol offset: {}", offset)); + } + // The offset specifies the location of rustc metadata in the comment section. + // The metadata is preceded by a 4-byte length field. + let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize; + if offset + len > (info_data.len() as usize) { + return Err(format!( + "Metadata at offset {} with size {} is beyond .info section", + offset, len + )); + } + return Ok(&info_data[offset..(offset + len)]); + } else { + return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME)); + }; +} + pub(crate) fn create_object_file(sess: &Session) -> Option> { let endianness = match sess.target.options.endian { Endian::Little => Endianness::Little, @@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { file.section_mut(section).flags = @@ -333,6 +376,30 @@ pub fn create_wrapper_file( file.section_mut(section).flags = SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 }; } + BinaryFormat::Xcoff => { + file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + file.section_mut(section).flags = + SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 }; + + let len = data.len() as u32; + let offset = file.append_section_data(section, &len.to_be_bytes(), 1); + // Add a symbol referring to the data in .info section. + file.add_symbol(Symbol { + name: AIX_METADATA_SYMBOL_NAME.into(), + value: offset + 4, + size: 0, + kind: SymbolKind::Unknown, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section), + flags: SymbolFlags::Xcoff { + n_sclass: xcoff::C_INFO, + x_smtyp: xcoff::C_HIDEXT, + x_smclas: xcoff::C_HIDEXT, + containing_csect: None, + }, + }); + } _ => {} }; file.append_section_data(section, data, 1); @@ -369,6 +436,9 @@ pub fn create_compressed_metadata_file( let Some(mut file) = create_object_file(sess) else { return compressed.to_vec(); }; + if file.format() == BinaryFormat::Xcoff { + return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name); + } let section = file.add_section( file.segment_name(StandardSegment::Data).to_vec(), b".rustc".to_vec(), @@ -398,3 +468,60 @@ pub fn create_compressed_metadata_file( file.write().unwrap() } + +/// * Xcoff - On AIX, custom sections are merged into predefined sections, +/// so custom .rustc section is not preserved during linking. +/// For this reason, we store metadata in predefined .info section, and +/// define a symbol to reference the metadata. To preserve metadata during +/// linking on AIX, we have to +/// 1. Create an empty .text section, a empty .data section. +/// 2. Define an empty symbol named `symbol_name` inside .data section. +/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing +/// data inside .info section. +/// From XCOFF's view, (2) creates a csect entry in the symbol table, the +/// symbol created by (3) is a info symbol for the preceding csect. Thus +/// two symbols are preserved during linking and we can use the second symbol +/// to reference the metadata. +pub fn create_compressed_metadata_file_for_xcoff( + mut file: write::Object<'_>, + data: &[u8], + symbol_name: &str, +) -> Vec { + assert!(file.format() == BinaryFormat::Xcoff); + file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); + let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug); + file.add_file_symbol("lib.rmeta".into()); + file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 }; + // Add a global symbol to data_section. + file.add_symbol(Symbol { + name: symbol_name.as_bytes().into(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Dynamic, + weak: true, + section: SymbolSection::Section(data_section), + flags: SymbolFlags::None, + }); + let len = data.len() as u32; + let offset = file.append_section_data(section, &len.to_be_bytes(), 1); + // Add a symbol referring to the rustc metadata. + file.add_symbol(Symbol { + name: AIX_METADATA_SYMBOL_NAME.into(), + value: offset + 4, // The metadata is preceded by a 4-byte length field. + size: 0, + kind: SymbolKind::Unknown, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section), + flags: SymbolFlags::Xcoff { + n_sclass: xcoff::C_INFO, + x_smtyp: xcoff::C_HIDEXT, + x_smclas: xcoff::C_HIDEXT, + containing_csect: None, + }, + }); + file.append_section_data(section, data, 1); + file.write().unwrap() +} From 5fdeae610db96d048090b1efaf6b7a2f54aeda12 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 23 May 2023 15:11:17 +0200 Subject: [PATCH 478/806] codegen: allow extra attributes to functions when panic=abort When compiling with panic=abort (or using a target that doesn't have unwinding support), the compiler adds the "nounwind" attribute to functions. This results in a different LLVM IR, which results in a #NNN added after the function name: tail call void @bar() #13, !dbg !467 attributes #13 = { nounwind } ...instead of: tail call void @bar(), !dbg !467 This commit changes the matchers to swallow the #NNN, as it's not needed for these specific tests. --- tests/codegen/call-metadata.rs | 2 +- tests/codegen/debug-column.rs | 4 ++-- tests/codegen/mir-inlined-line-numbers.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/codegen/call-metadata.rs b/tests/codegen/call-metadata.rs index 1c30c08d3b2e2..07cc0c963717e 100644 --- a/tests/codegen/call-metadata.rs +++ b/tests/codegen/call-metadata.rs @@ -6,7 +6,7 @@ #![crate_type = "lib"] pub fn test() { - // CHECK: call noundef i8 @some_true(), !range [[R0:![0-9]+]] + // CHECK: call noundef i8 @some_true(){{( #[0-9]+)?}}, !range [[R0:![0-9]+]] // CHECK: [[R0]] = !{i8 0, i8 3} some_true(); } diff --git a/tests/codegen/debug-column.rs b/tests/codegen/debug-column.rs index e61642b8e1be2..f3b19a2eb2f42 100644 --- a/tests/codegen/debug-column.rs +++ b/tests/codegen/debug-column.rs @@ -6,11 +6,11 @@ fn main() { unsafe { // Column numbers are 1-based. Regression test for #65437. - // CHECK: call void @giraffe(), !dbg [[A:!.*]] + // CHECK: call void @giraffe(){{( #[0-9]+)?}}, !dbg [[A:!.*]] giraffe(); // Column numbers use byte offests. Regression test for #67360 - // CHECK: call void @turtle(), !dbg [[B:!.*]] + // CHECK: call void @turtle(){{( #[0-9]+)?}}, !dbg [[B:!.*]] /* ż */ turtle(); // CHECK: [[A]] = !DILocation(line: 10, column: 9, diff --git a/tests/codegen/mir-inlined-line-numbers.rs b/tests/codegen/mir-inlined-line-numbers.rs index 19d83f0eee7c4..d13527b952130 100644 --- a/tests/codegen/mir-inlined-line-numbers.rs +++ b/tests/codegen/mir-inlined-line-numbers.rs @@ -19,7 +19,7 @@ pub fn example() { } // CHECK-LABEL: @example -// CHECK: tail call void @bar(), !dbg [[DBG_ID:![0-9]+]] +// CHECK: tail call void @bar(){{( #[0-9]+)?}}, !dbg [[DBG_ID:![0-9]+]] // CHECK: [[DBG_ID]] = !DILocation(line: 7, // CHECK-SAME: inlinedAt: [[INLINE_ID:![0-9]+]]) // CHECK: [[INLINE_ID]] = !DILocation(line: 18, From dc1ed9ddd7d15dfe75453b68bc1da8ae32dbe1f7 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 23 May 2023 15:26:31 +0200 Subject: [PATCH 479/806] codegen: allow the dso_local attribute The attribute is injected into most items when static relocation is enabled in a target. --- tests/codegen/box-maybe-uninit-llvm14.rs | 2 +- tests/codegen/box-maybe-uninit.rs | 2 +- tests/codegen/external-no-mangle-statics.rs | 32 ++++++++++----------- tests/codegen/issues/issue-86106.rs | 4 +-- tests/codegen/link_section.rs | 8 +++--- tests/codegen/naked-noinline.rs | 4 +-- tests/codegen/ptr-read-metadata.rs | 16 +++++------ tests/codegen/tuple-layout-opt.rs | 12 ++++---- tests/codegen/union-abi.rs | 20 ++++++------- 9 files changed, 50 insertions(+), 50 deletions(-) diff --git a/tests/codegen/box-maybe-uninit-llvm14.rs b/tests/codegen/box-maybe-uninit-llvm14.rs index b0c88f76c436d..c9f88fb3fe452 100644 --- a/tests/codegen/box-maybe-uninit-llvm14.rs +++ b/tests/codegen/box-maybe-uninit-llvm14.rs @@ -31,4 +31,4 @@ pub fn box_uninitialized2() -> Box> { // Hide the LLVM 15+ `allocalign` attribute in the declaration of __rust_alloc // from the CHECK-NOT above. We don't check the attributes here because we can't rely // on all of them being set until LLVM 15. -// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef) +// CHECK: declare {{(dso_local )?}}noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef) diff --git a/tests/codegen/box-maybe-uninit.rs b/tests/codegen/box-maybe-uninit.rs index 2f88966996ab2..9f079e99b9c76 100644 --- a/tests/codegen/box-maybe-uninit.rs +++ b/tests/codegen/box-maybe-uninit.rs @@ -28,6 +28,6 @@ pub fn box_uninitialized2() -> Box> { // Hide the `allocalign` attribute in the declaration of __rust_alloc // from the CHECK-NOT above, and also verify the attributes got set reasonably. -// CHECK: declare noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] +// CHECK: declare {{(dso_local )?}}noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] // CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} } diff --git a/tests/codegen/external-no-mangle-statics.rs b/tests/codegen/external-no-mangle-statics.rs index c6ecb7aa96abc..48023a2a901cd 100644 --- a/tests/codegen/external-no-mangle-statics.rs +++ b/tests/codegen/external-no-mangle-statics.rs @@ -6,72 +6,72 @@ // `#[no_mangle]`d static variables always have external linkage, i.e., no `internal` in their // definitions -// CHECK: @A = local_unnamed_addr constant +// CHECK: @A = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static A: u8 = 0; -// CHECK: @B = local_unnamed_addr global +// CHECK: @B = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut B: u8 = 0; -// CHECK: @C = local_unnamed_addr constant +// CHECK: @C = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static C: u8 = 0; -// CHECK: @D = local_unnamed_addr global +// CHECK: @D = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut D: u8 = 0; mod private { - // CHECK: @E = local_unnamed_addr constant + // CHECK: @E = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static E: u8 = 0; - // CHECK: @F = local_unnamed_addr global + // CHECK: @F = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut F: u8 = 0; - // CHECK: @G = local_unnamed_addr constant + // CHECK: @G = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static G: u8 = 0; - // CHECK: @H = local_unnamed_addr global + // CHECK: @H = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut H: u8 = 0; } const HIDDEN: () = { - // CHECK: @I = local_unnamed_addr constant + // CHECK: @I = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static I: u8 = 0; - // CHECK: @J = local_unnamed_addr global + // CHECK: @J = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut J: u8 = 0; - // CHECK: @K = local_unnamed_addr constant + // CHECK: @K = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static K: u8 = 0; - // CHECK: @L = local_unnamed_addr global + // CHECK: @L = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut L: u8 = 0; }; fn x() { - // CHECK: @M = local_unnamed_addr constant + // CHECK: @M = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static M: fn() = x; - // CHECK: @N = local_unnamed_addr global + // CHECK: @N = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut N: u8 = 0; - // CHECK: @O = local_unnamed_addr constant + // CHECK: @O = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static O: u8 = 0; - // CHECK: @P = local_unnamed_addr global + // CHECK: @P = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut P: u8 = 0; } diff --git a/tests/codegen/issues/issue-86106.rs b/tests/codegen/issues/issue-86106.rs index c0be7fab2f3e0..be5034dcfbd7e 100644 --- a/tests/codegen/issues/issue-86106.rs +++ b/tests/codegen/issues/issue-86106.rs @@ -7,7 +7,7 @@ #![crate_type = "lib"] -// CHECK-LABEL: define void @string_new +// CHECK-LABEL: define {{(dso_local )?}}void @string_new #[no_mangle] pub fn string_new() -> String { // CHECK: store ptr inttoptr @@ -17,7 +17,7 @@ pub fn string_new() -> String { String::new() } -// CHECK-LABEL: define void @empty_to_string +// CHECK-LABEL: define {{(dso_local )?}}void @empty_to_string #[no_mangle] pub fn empty_to_string() -> String { // CHECK: store ptr inttoptr diff --git a/tests/codegen/link_section.rs b/tests/codegen/link_section.rs index 88b8692b0ac54..2b26b604ad323 100644 --- a/tests/codegen/link_section.rs +++ b/tests/codegen/link_section.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" +// CHECK: @VAR1 = {{(dso_local )?}}constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] #[cfg(target_endian = "little")] @@ -19,17 +19,17 @@ pub enum E { B(f32) } -// CHECK: @VAR2 = constant {{.*}}, section ".test_two" +// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two" #[no_mangle] #[link_section = ".test_two"] pub static VAR2: E = E::A(666); -// CHECK: @VAR3 = constant {{.*}}, section ".test_three" +// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section ".test_three" #[no_mangle] #[link_section = ".test_three"] pub static VAR3: E = E::B(1.); -// CHECK: define void @fn1() {{.*}} section ".test_four" { +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section ".test_four" { #[no_mangle] #[link_section = ".test_four"] pub fn fn1() {} diff --git a/tests/codegen/naked-noinline.rs b/tests/codegen/naked-noinline.rs index c0ac69f4ed778..5cfb500c0ef88 100644 --- a/tests/codegen/naked-noinline.rs +++ b/tests/codegen/naked-noinline.rs @@ -12,7 +12,7 @@ use std::arch::asm; pub unsafe extern "C" fn f() { // Check that f has naked and noinline attributes. // - // CHECK: define void @f() unnamed_addr [[ATTR:#[0-9]+]] + // CHECK: define {{(dso_local )?}}void @f() unnamed_addr [[ATTR:#[0-9]+]] // CHECK-NEXT: start: // CHECK-NEXT: call void asm asm!("", options(noreturn)); @@ -22,7 +22,7 @@ pub unsafe extern "C" fn f() { pub unsafe fn g() { // Check that call to f is not inlined. // - // CHECK-LABEL: define void @g() + // CHECK-LABEL: define {{(dso_local )?}}void @g() // CHECK-NEXT: start: // CHECK-NEXT: call void @f() f(); diff --git a/tests/codegen/ptr-read-metadata.rs b/tests/codegen/ptr-read-metadata.rs index e1e3272662c4b..73d1db6df277b 100644 --- a/tests/codegen/ptr-read-metadata.rs +++ b/tests/codegen/ptr-read-metadata.rs @@ -9,7 +9,7 @@ use std::mem::MaybeUninit; -// CHECK-LABEL: define noundef i8 @copy_byte( +// CHECK-LABEL: define {{(dso_local )?}}noundef i8 @copy_byte( #[no_mangle] pub unsafe fn copy_byte(p: *const u8) -> u8 { // CHECK-NOT: load @@ -19,7 +19,7 @@ pub unsafe fn copy_byte(p: *const u8) -> u8 { *p } -// CHECK-LABEL: define noundef i8 @read_byte( +// CHECK-LABEL: define {{(dso_local )?}}noundef i8 @read_byte( #[no_mangle] pub unsafe fn read_byte(p: *const u8) -> u8 { // CHECK-NOT: load @@ -29,7 +29,7 @@ pub unsafe fn read_byte(p: *const u8) -> u8 { p.read() } -// CHECK-LABEL: define i8 @read_byte_maybe_uninit( +// CHECK-LABEL: define {{(dso_local )?}}i8 @read_byte_maybe_uninit( #[no_mangle] pub unsafe fn read_byte_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit { // CHECK-NOT: load @@ -39,7 +39,7 @@ pub unsafe fn read_byte_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit) -> u8 { // CHECK-NOT: load @@ -49,7 +49,7 @@ pub unsafe fn read_byte_assume_init(p: &MaybeUninit) -> u8 { p.assume_init_read() } -// CHECK-LABEL: define noundef i32 @copy_char( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @copy_char( #[no_mangle] pub unsafe fn copy_char(p: *const char) -> char { // CHECK-NOT: load @@ -60,7 +60,7 @@ pub unsafe fn copy_char(p: *const char) -> char { *p } -// CHECK-LABEL: define noundef i32 @read_char( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @read_char( #[no_mangle] pub unsafe fn read_char(p: *const char) -> char { // CHECK-NOT: load @@ -71,7 +71,7 @@ pub unsafe fn read_char(p: *const char) -> char { p.read() } -// CHECK-LABEL: define i32 @read_char_maybe_uninit( +// CHECK-LABEL: define {{(dso_local )?}}i32 @read_char_maybe_uninit( #[no_mangle] pub unsafe fn read_char_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit { // CHECK-NOT: load @@ -82,7 +82,7 @@ pub unsafe fn read_char_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit p.read() } -// CHECK-LABEL: define noundef i32 @read_char_assume_init( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @read_char_assume_init( #[no_mangle] pub unsafe fn read_char_assume_init(p: &MaybeUninit) -> char { // CHECK-NOT: load diff --git a/tests/codegen/tuple-layout-opt.rs b/tests/codegen/tuple-layout-opt.rs index 35f760851451e..309fe1d5ec902 100644 --- a/tests/codegen/tuple-layout-opt.rs +++ b/tests/codegen/tuple-layout-opt.rs @@ -6,31 +6,31 @@ #![crate_type="lib"] type ScalarZstLast = (u128, ()); -// CHECK: define i128 @test_ScalarZstLast(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstLast(i128 %_1) #[no_mangle] pub fn test_ScalarZstLast(_: ScalarZstLast) -> ScalarZstLast { loop {} } type ScalarZstFirst = ((), u128); -// CHECK: define i128 @test_ScalarZstFirst(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstFirst(i128 %_1) #[no_mangle] pub fn test_ScalarZstFirst(_: ScalarZstFirst) -> ScalarZstFirst { loop {} } type ScalarPairZstLast = (u8, u128, ()); -// CHECK: define { i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairZstLast(_: ScalarPairZstLast) -> ScalarPairZstLast { loop {} } type ScalarPairZstFirst = ((), u8, u128); -// CHECK: define { i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) +// CHECK: define {{(dso_local )?}}{ i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) #[no_mangle] pub fn test_ScalarPairZstFirst(_: ScalarPairZstFirst) -> ScalarPairZstFirst { loop {} } type ScalarPairLotsOfZsts = ((), u8, (), u128, ()); -// CHECK: define { i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLotsOfZsts(_: ScalarPairLotsOfZsts) -> ScalarPairLotsOfZsts { loop {} } type ScalarPairLottaNesting = (((), ((), u8, (), u128, ())), ()); -// CHECK: define { i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLottaNesting(_: ScalarPairLottaNesting) -> ScalarPairLottaNesting { loop {} } diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs index c18f2a49fc369..8481ca8ccfa19 100644 --- a/tests/codegen/union-abi.rs +++ b/tests/codegen/union-abi.rs @@ -17,60 +17,60 @@ pub struct i64x4(i64, i64, i64, i64); #[derive(Copy, Clone)] pub union UnionI64x4{ a:(), b: i64x4 } -// CHECK: define void @test_UnionI64x4({{<4 x i64>\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4({{<4 x i64>\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4(_: UnionI64x4) { loop {} } pub union UnionI64x4_{ a: i64x4, b: (), c:i64x4, d: Unhab, e: ((),()), f: UnionI64x4 } -// CHECK: define void @test_UnionI64x4_({{<4 x i64>\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4_({{<4 x i64>\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4_(_: UnionI64x4_) { loop {} } pub union UnionI64x4I64{ a: i64x4, b: i64 } -// CHECK: define void @test_UnionI64x4I64({{%UnionI64x4I64\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4I64({{%UnionI64x4I64\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4I64(_: UnionI64x4I64) { loop {} } pub union UnionI64x4Tuple{ a: i64x4, b: (i64, i64, i64, i64) } -// CHECK: define void @test_UnionI64x4Tuple({{%UnionI64x4Tuple\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4Tuple({{%UnionI64x4Tuple\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4Tuple(_: UnionI64x4Tuple) { loop {} } pub union UnionF32{a:f32} -// CHECK: define float @test_UnionF32(float %_1) +// CHECK: define {{(dso_local )?}}float @test_UnionF32(float %_1) #[no_mangle] pub fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} } pub union UnionF32F32{a:f32, b:f32} -// CHECK: define float @test_UnionF32F32(float %_1) +// CHECK: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) #[no_mangle] pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { loop {} } pub union UnionF32U32{a:f32, b:u32} -// CHECK: define i32 @test_UnionF32U32(i32{{( %0)?}}) +// CHECK: define {{(dso_local )?}}i32 @test_UnionF32U32(i32{{( %0)?}}) #[no_mangle] pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { loop {} } pub union UnionU128{a:u128} -// CHECK: define i128 @test_UnionU128(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_UnionU128(i128 %_1) #[no_mangle] pub fn test_UnionU128(_: UnionU128) -> UnionU128 { loop {} } #[repr(C)] pub union CUnionU128{a:u128} -// CHECK: define void @test_CUnionU128({{%CUnionU128\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_CUnionU128({{%CUnionU128\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_CUnionU128(_: CUnionU128) { loop {} } pub union UnionBool { b:bool } -// CHECK: define noundef zeroext i1 @test_UnionBool(i8 %b) +// CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8 %b) #[no_mangle] pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } } // CHECK: %0 = trunc i8 %b to i1 From 292bc548c882686f40886934f2055f3440a87eca Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 23 May 2023 15:27:05 +0200 Subject: [PATCH 480/806] codegen: do not require the uwtables attribute The attribute is not emitted on targets without unwinding tables. --- tests/codegen/box-maybe-uninit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/box-maybe-uninit.rs b/tests/codegen/box-maybe-uninit.rs index 9f079e99b9c76..5c08b5832ad80 100644 --- a/tests/codegen/box-maybe-uninit.rs +++ b/tests/codegen/box-maybe-uninit.rs @@ -30,4 +30,4 @@ pub fn box_uninitialized2() -> Box> { // from the CHECK-NOT above, and also verify the attributes got set reasonably. // CHECK: declare {{(dso_local )?}}noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] -// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} } +// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} } From 5f0b677b86204c4cd98cf51b33b7224340406719 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 23 May 2023 15:20:57 +0200 Subject: [PATCH 481/806] codegen: add needs-unwind to tests that require it --- tests/codegen/drop.rs | 1 + tests/codegen/personality_lifetimes.rs | 1 + tests/codegen/unwind-abis/c-unwind-abi.rs | 1 + tests/codegen/unwind-abis/cdecl-unwind-abi.rs | 1 + tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs | 1 + tests/codegen/unwind-abis/system-unwind-abi.rs | 1 + tests/codegen/unwind-extern-exports.rs | 1 + tests/codegen/unwind-extern-imports.rs | 1 + tests/codegen/vec-shrink-panik.rs | 1 + 9 files changed, 9 insertions(+) diff --git a/tests/codegen/drop.rs b/tests/codegen/drop.rs index 994028271583f..3615ef47b531b 100644 --- a/tests/codegen/drop.rs +++ b/tests/codegen/drop.rs @@ -1,4 +1,5 @@ // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind - this test verifies the amount of drop calls when unwinding is used // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] diff --git a/tests/codegen/personality_lifetimes.rs b/tests/codegen/personality_lifetimes.rs index 2104022f57874..9ff7a9b3e8879 100644 --- a/tests/codegen/personality_lifetimes.rs +++ b/tests/codegen/personality_lifetimes.rs @@ -1,5 +1,6 @@ // ignore-msvc // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind // compile-flags: -O -C no-prepopulate-passes diff --git a/tests/codegen/unwind-abis/c-unwind-abi.rs b/tests/codegen/unwind-abis/c-unwind-abi.rs index e258dbcacd229..fa5b6bad75cb3 100644 --- a/tests/codegen/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen/unwind-abis/c-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `C` and `C-unwind` extern // functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above diff --git a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs index 19a7228839abf..64746d32175be 100644 --- a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `cdecl` and // `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We diff --git a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs b/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs index c1c5bbdda3456..dc3911cd4ebac 100644 --- a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs +++ b/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs @@ -1,5 +1,6 @@ // compile-flags: -C opt-level=0 // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] diff --git a/tests/codegen/unwind-abis/system-unwind-abi.rs b/tests/codegen/unwind-abis/system-unwind-abi.rs index 2591c1d481430..f274a33b09948 100644 --- a/tests/codegen/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen/unwind-abis/system-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `system` and `system-unwind` // extern functions. `system-unwind` functions MUST NOT have this attribute. We disable diff --git a/tests/codegen/unwind-extern-exports.rs b/tests/codegen/unwind-extern-exports.rs index 6ac3c079f81ba..4e1e719d5cd18 100644 --- a/tests/codegen/unwind-extern-exports.rs +++ b/tests/codegen/unwind-extern-exports.rs @@ -1,5 +1,6 @@ // compile-flags: -C opt-level=0 // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] #![feature(c_unwind)] diff --git a/tests/codegen/unwind-extern-imports.rs b/tests/codegen/unwind-extern-imports.rs index e33e3e80521c1..260dcc628cc0e 100644 --- a/tests/codegen/unwind-extern-imports.rs +++ b/tests/codegen/unwind-extern-imports.rs @@ -1,5 +1,6 @@ // compile-flags: -C no-prepopulate-passes // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] #![feature(c_unwind)] diff --git a/tests/codegen/vec-shrink-panik.rs b/tests/codegen/vec-shrink-panik.rs index 88b7edff26093..606d68ff3ab38 100644 --- a/tests/codegen/vec-shrink-panik.rs +++ b/tests/codegen/vec-shrink-panik.rs @@ -5,6 +5,7 @@ // [new]min-llvm-version: 17 // compile-flags: -O // ignore-debug: the debug assertions get in the way +// needs-unwind #![crate_type = "lib"] #![feature(shrink_to)] From 8ef6240afb00ec9a892cb41c36cde102b688343a Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 23 May 2023 17:22:23 +0200 Subject: [PATCH 482/806] point to `await` expr in note --- clippy_lints/src/unused_async.rs | 16 ++++++++++------ tests/ui/unused_async.stderr | 6 +++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 4c68cda7aa714..117dda092223e 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -44,7 +44,7 @@ struct AsyncFnVisitor<'a, 'tcx> { found_await: bool, /// Also keep track of `await`s in nested async blocks so we can mention /// it in a note - found_await_in_async_block: bool, + await_in_async_block: Option, async_depth: usize, } @@ -55,8 +55,8 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { if self.async_depth == 1 { self.found_await = true; - } else { - self.found_await_in_async_block = true; + } else if self.await_in_async_block.is_none() { + self.await_in_async_block = Some(ex.span); } } walk_expr(self, ex); @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { cx, found_await: false, async_depth: 0, - found_await_in_async_block: false, + await_in_async_block: None, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -108,8 +108,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { |diag| { diag.help("consider removing the `async` from this function"); - if visitor.found_await_in_async_block { - diag.note("`await` used in an async block, which does not require the enclosing function to be `async`"); + if let Some(span) = visitor.await_in_async_block { + diag.span_note( + span, + "`await` used in an async block, which does not require \ + the enclosing function to be `async`", + ); } }, ); diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr index e39f9b20b8816..8ac2066a6b24d 100644 --- a/tests/ui/unused_async.stderr +++ b/tests/ui/unused_async.stderr @@ -9,7 +9,11 @@ LL | | } | |_____^ | = help: consider removing the `async` from this function - = note: `await` used in an async block, which does not require the enclosing function to be `async` +note: `await` used in an async block, which does not require the enclosing function to be `async` + --> $DIR/unused_async.rs:13:23 + | +LL | ready(()).await; + | ^^^^^ = note: `-D clippy::unused-async` implied by `-D warnings` error: unused `async` for function with no await statements From a3438da42f0806225f7533f282ea32b9416c38f8 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 23 May 2023 21:11:56 +0200 Subject: [PATCH 483/806] error out if lint name contains dash --- clippy_dev/src/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index e2457e5a8a5e9..41fdbe099aec0 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -180,7 +180,14 @@ fn get_clap_config() -> ArgMatches { .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .required(true), + .required(true) + .value_parser(|name: &str| { + if name.contains('-') { + Err("Lint name cannot contain `-`, use `_` instead.") + } else { + Ok(name.to_owned()) + } + }), Arg::new("category") .short('c') .long("category") From 47a024e81d288323def492d07de79aae9550ff1e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 23 May 2023 22:28:14 +0000 Subject: [PATCH 484/806] Display the `needless_return` suggestion --- clippy_lints/src/returns.rs | 28 ++- tests/ui/needless_return.stderr | 322 ++++++++++++++++++++++++++------ 2 files changed, 283 insertions(+), 67 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index df126d7617ebe..631ecf1428d6a 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -70,7 +70,7 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq)] enum RetReplacement<'tcx> { Empty, Block, @@ -80,7 +80,7 @@ enum RetReplacement<'tcx> { } impl<'tcx> RetReplacement<'tcx> { - fn sugg_help(self) -> &'static str { + fn sugg_help(&self) -> &'static str { match self { Self::Empty | Self::Expr(..) => "remove `return`", Self::Block => "replace `return` with an empty block", @@ -88,10 +88,11 @@ impl<'tcx> RetReplacement<'tcx> { Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses", } } - fn applicability(&self) -> Option { + + fn applicability(&self) -> Applicability { match self { - Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap), - _ => None, + Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap, + _ => Applicability::MachineApplicable, } } } @@ -271,7 +272,7 @@ fn check_final_expr<'tcx>( return; } - emit_return_lint(cx, ret_span, semi_spans, replacement); + emit_return_lint(cx, ret_span, semi_spans, &replacement); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -306,20 +307,17 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { contains_if(expr, false) } -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, replacement: RetReplacement<'_>) { +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>) { if ret_span.from_expansion() { return; } - let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable); - let return_replacement = replacement.to_string(); - let sugg_help = replacement.sugg_help(); span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); - // for each parent statement, we need to remove the semicolon - for semi_stmt_span in semi_spans { - diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability); - } + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); }); } diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index b5a7d61139d8d..1d9d23d30083c 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -5,7 +5,11 @@ LL | return true; | ^^^^^^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:32:5 @@ -13,7 +17,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:37:5 @@ -21,7 +29,11 @@ error: unneeded `return` statement LL | return true;;; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true;;; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:42:5 @@ -29,7 +41,11 @@ error: unneeded `return` statement LL | return true;; ; ; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true;; ; ; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:47:9 @@ -37,7 +53,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:49:9 @@ -45,7 +65,11 @@ error: unneeded `return` statement LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return false; +LL + false + | error: unneeded `return` statement --> $DIR/needless_return.rs:55:17 @@ -53,7 +77,10 @@ error: unneeded `return` statement LL | true => return false, | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | true => false, + | ~~~~~ error: unneeded `return` statement --> $DIR/needless_return.rs:57:13 @@ -61,7 +88,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:64:9 @@ -69,7 +100,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:66:16 @@ -77,7 +112,10 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | let _ = || true; + | ~~~~ error: unneeded `return` statement --> $DIR/needless_return.rs:70:5 @@ -85,7 +123,11 @@ error: unneeded `return` statement LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return the_answer!(); +LL + the_answer!() + | error: unneeded `return` statement --> $DIR/needless_return.rs:73:21 @@ -95,7 +137,12 @@ LL | fn test_void_fun() { LL | | return; | |__________^ | - = help: remove `return` +help: remove `return` + | +LL - fn test_void_fun() { +LL - return; +LL + fn test_void_fun() { + | error: unneeded `return` statement --> $DIR/needless_return.rs:78:11 @@ -105,7 +152,12 @@ LL | if b { LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - if b { +LL - return; +LL + if b { + | error: unneeded `return` statement --> $DIR/needless_return.rs:80:13 @@ -115,7 +167,12 @@ LL | } else { LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - } else { +LL - return; +LL + } else { + | error: unneeded `return` statement --> $DIR/needless_return.rs:88:14 @@ -123,7 +180,10 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement --> $DIR/needless_return.rs:96:24 @@ -133,7 +193,12 @@ LL | let _ = 42; LL | | return; | |__________________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; +LL - return; +LL + let _ = 42; + | error: unneeded `return` statement --> $DIR/needless_return.rs:99:14 @@ -141,7 +206,10 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement --> $DIR/needless_return.rs:112:9 @@ -149,7 +217,11 @@ error: unneeded `return` statement LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::from("test"); +LL + String::from("test") + | error: unneeded `return` statement --> $DIR/needless_return.rs:114:9 @@ -157,7 +229,11 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::new(); +LL + String::new() + | error: unneeded `return` statement --> $DIR/needless_return.rs:136:32 @@ -165,7 +241,10 @@ error: unneeded `return` statement LL | bar.unwrap_or_else(|_| return) | ^^^^^^ | - = help: replace `return` with an empty block +help: replace `return` with an empty block + | +LL | bar.unwrap_or_else(|_| {}) + | ~~ error: unneeded `return` statement --> $DIR/needless_return.rs:140:21 @@ -175,7 +254,12 @@ LL | let _ = || { LL | | return; | |__________________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = || { +LL - return; +LL + let _ = || { + | error: unneeded `return` statement --> $DIR/needless_return.rs:143:20 @@ -183,7 +267,10 @@ error: unneeded `return` statement LL | let _ = || return; | ^^^^^^ | - = help: replace `return` with an empty block +help: replace `return` with an empty block + | +LL | let _ = || {}; + | ~~ error: unneeded `return` statement --> $DIR/needless_return.rs:149:32 @@ -191,7 +278,10 @@ error: unneeded `return` statement LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | res.unwrap_or_else(|_| Foo) + | ~~~ error: unneeded `return` statement --> $DIR/needless_return.rs:158:5 @@ -199,7 +289,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:162:5 @@ -207,7 +301,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:167:9 @@ -215,7 +313,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:169:9 @@ -223,7 +325,11 @@ error: unneeded `return` statement LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return false; +LL + false + | error: unneeded `return` statement --> $DIR/needless_return.rs:175:17 @@ -231,7 +337,10 @@ error: unneeded `return` statement LL | true => return false, | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | true => false, + | ~~~~~ error: unneeded `return` statement --> $DIR/needless_return.rs:177:13 @@ -239,7 +348,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:184:9 @@ -247,7 +360,11 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement --> $DIR/needless_return.rs:186:16 @@ -255,7 +372,10 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | let _ = || true; + | ~~~~ error: unneeded `return` statement --> $DIR/needless_return.rs:190:5 @@ -263,7 +383,11 @@ error: unneeded `return` statement LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return the_answer!(); +LL + the_answer!() + | error: unneeded `return` statement --> $DIR/needless_return.rs:193:33 @@ -273,7 +397,12 @@ LL | async fn async_test_void_fun() { LL | | return; | |__________^ | - = help: remove `return` +help: remove `return` + | +LL - async fn async_test_void_fun() { +LL - return; +LL + async fn async_test_void_fun() { + | error: unneeded `return` statement --> $DIR/needless_return.rs:198:11 @@ -283,7 +412,12 @@ LL | if b { LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - if b { +LL - return; +LL + if b { + | error: unneeded `return` statement --> $DIR/needless_return.rs:200:13 @@ -293,7 +427,12 @@ LL | } else { LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - } else { +LL - return; +LL + } else { + | error: unneeded `return` statement --> $DIR/needless_return.rs:208:14 @@ -301,7 +440,10 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement --> $DIR/needless_return.rs:221:9 @@ -309,7 +451,11 @@ error: unneeded `return` statement LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::from("test"); +LL + String::from("test") + | error: unneeded `return` statement --> $DIR/needless_return.rs:223:9 @@ -317,7 +463,11 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::new(); +LL + String::new() + | error: unneeded `return` statement --> $DIR/needless_return.rs:239:5 @@ -325,7 +475,11 @@ error: unneeded `return` statement LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return format!("Hello {}", "world!"); +LL + format!("Hello {}", "world!") + | error: unneeded `return` statement --> $DIR/needless_return.rs:251:9 @@ -333,7 +487,13 @@ error: unneeded `return` statement LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ true +LL | } else { +LL | return false; +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:253:9 @@ -341,7 +501,11 @@ error: unneeded `return` statement LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ false +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:260:13 @@ -349,7 +513,14 @@ error: unneeded `return` statement LL | return 10; | ^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 10 +LL | }, + ... +LL | }, +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:263:13 @@ -357,7 +528,12 @@ error: unneeded `return` statement LL | return 100; | ^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 100 +LL | }, +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:271:9 @@ -365,7 +541,11 @@ error: unneeded `return` statement LL | return 0; | ^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 0 +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:278:13 @@ -373,7 +553,14 @@ error: unneeded `return` statement LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ *(x as *const isize) +LL | } else { +LL | return !*(x as *const isize); +LL ~ } +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:280:13 @@ -381,7 +568,12 @@ error: unneeded `return` statement LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ !*(x as *const isize) +LL ~ } +LL ~ } + | error: unneeded `return` statement --> $DIR/needless_return.rs:287:20 @@ -392,7 +584,13 @@ LL | | LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; +LL - +LL - return; +LL + let _ = 42; + | error: unneeded `return` statement --> $DIR/needless_return.rs:294:20 @@ -400,7 +598,11 @@ error: unneeded `return` statement LL | let _ = 42; return; | ^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; return; +LL + let _ = 42; + | error: unneeded `return` statement --> $DIR/needless_return.rs:306:9 @@ -408,7 +610,11 @@ error: unneeded `return` statement LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return Ok(format!("ok!")); +LL + Ok(format!("ok!")) + | error: unneeded `return` statement --> $DIR/needless_return.rs:308:9 @@ -416,7 +622,11 @@ error: unneeded `return` statement LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return Err(format!("err!")); +LL + Err(format!("err!")) + | error: unneeded `return` statement --> $DIR/needless_return.rs:314:9 @@ -424,7 +634,11 @@ error: unneeded `return` statement LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return if true { 1 } else { 2 }; +LL + if true { 1 } else { 2 } + | error: unneeded `return` statement --> $DIR/needless_return.rs:318:9 @@ -432,7 +646,11 @@ error: unneeded `return` statement LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` and wrap the sequence with parentheses +help: remove `return` and wrap the sequence with parentheses + | +LL - return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; +LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }) + | error: aborting due to 52 previous errors From 12d4355c60954e5c1b744ea49ca2652f1df16d9a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 24 May 2023 12:54:25 +0200 Subject: [PATCH 485/806] Remove double lookups from Interned --- crates/intern/src/lib.rs | 91 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/crates/intern/src/lib.rs b/crates/intern/src/lib.rs index d17eddf1560a1..dabbf3a38b502 100644 --- a/crates/intern/src/lib.rs +++ b/crates/intern/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; use dashmap::{DashMap, SharedValue}; -use hashbrown::HashMap; +use hashbrown::{hash_map::RawEntryMut, HashMap}; use once_cell::sync::OnceCell; use rustc_hash::FxHasher; use triomphe::Arc; @@ -26,56 +26,58 @@ pub struct Interned { impl Interned { pub fn new(obj: T) -> Self { - match Interned::lookup(&obj) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::new(obj); - Self::alloc(arc, shard) - } + let (mut shard, hash) = Self::select(&obj); + // Atomically, + // - check if `obj` is already in the map + // - if so, clone its `Arc` and return it + // - if not, box it up, insert it, and return a clone + // This needs to be atomic (locking the shard) to avoid races with other thread, which could + // insert the same object between us looking it up and inserting it. + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(())) + .0 + .clone(), + }, } } } -impl Interned { - fn lookup(obj: &T) -> Result> { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(obj); - let shard = &storage.shards()[shard_idx]; - let shard = shard.write(); - +impl Interned { + pub fn new_str(s: &str) -> Self { + let (mut shard, hash) = Self::select(s); // Atomically, // - check if `obj` is already in the map // - if so, clone its `Arc` and return it // - if not, box it up, insert it, and return a clone // This needs to be atomic (locking the shard) to avoid races with other thread, which could // insert the same object between us looking it up and inserting it. - - // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when - // hashbrown can be plugged into dashmap) - match shard.get_key_value(obj) { - Some((arc, _)) => Ok(Self { arc: arc.clone() }), - None => Err(shard), + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(())) + .0 + .clone(), + }, } } - - fn alloc(arc: Arc, mut shard: Guard) -> Self { - let arc2 = arc.clone(); - - shard.insert(arc2, SharedValue::new(())); - - Self { arc } - } } -impl Interned { - pub fn new_str(s: &str) -> Self { - match Interned::lookup(s) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::::from(s); - Self::alloc(arc, shard) - } - } +impl Interned { + #[inline] + fn select(obj: &T) -> (Guard, u64) { + let storage = T::storage().get(); + let hash = { + let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher()); + obj.hash(&mut hasher); + hasher.finish() + }; + let shard_idx = storage.determine_shard(hash as usize); + let shard = &storage.shards()[shard_idx]; + (shard.write(), hash) } } @@ -94,20 +96,17 @@ impl Drop for Interned { impl Interned { #[cold] fn drop_slow(&mut self) { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(&self.arc); - let shard = &storage.shards()[shard_idx]; - let mut shard = shard.write(); - - // FIXME: avoid double lookup - let (arc, _) = shard.get_key_value(&self.arc).expect("interned value removed prematurely"); + let (mut shard, hash) = Self::select(&self.arc); - if Arc::count(arc) != 2 { + if Arc::count(&self.arc) != 2 { // Another thread has interned another copy return; } - shard.remove(&self.arc); + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &self.arc) { + RawEntryMut::Occupied(occ) => occ.remove(), + RawEntryMut::Vacant(_) => unreachable!(), + }; // Shrink the backing storage if the shard is less than 50% occupied. if shard.len() * 2 < shard.capacity() { From 74d6826858bab734766048a670820d9961c70448 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 22 May 2023 21:19:54 +0200 Subject: [PATCH 486/806] expand: Change how `#![cfg(FALSE)]` behaves on crate root --- crates/hir-def/src/attr.rs | 1 + crates/hir-def/src/nameres/collector.rs | 10 +++++----- crates/hir-expand/src/attrs.rs | 8 ++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index de515b569dfa9..bab3bbc2329a9 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -202,6 +202,7 @@ impl Attrs { None => Some(first), } } + pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { match self.cfg() { None => true, diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index a47ee85da10ae..a528d238e377b 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -290,16 +290,16 @@ impl DefCollector<'_> { let module_id = self.def_map.root; let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); - if let Some(cfg) = attrs.cfg() { - if self.cfg_options.check(&cfg) == Some(false) { - return; - } - } self.inject_prelude(&attrs); // Process other crate-level attributes. for attr in &*attrs { + if let Some(cfg) = attr.cfg() { + if self.cfg_options.check(&cfg) == Some(false) { + return; + } + } let attr_name = match attr.path.as_ident() { Some(name) => name, None => continue, diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index 3ff18e982e186..0c369a18bb9cc 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -309,6 +309,14 @@ impl Attr { Some(paths) } + + pub fn cfg(&self) -> Option { + if *self.path.as_ident()? == crate::name![cfg] { + self.token_tree_value().map(CfgExpr::parse) + } else { + None + } + } } pub fn collect_attrs( From e005fcf09eb9a5cd5d7ca7c095435cf63eca0fa8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 24 May 2023 22:08:43 +0900 Subject: [PATCH 487/806] Address Dependabot alerts --- .../multiple_crate_versions/fail/Cargo.lock | 67 +------------------ .../multiple_crate_versions/fail/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 65 deletions(-) diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock index 7e96aa36feb45..e4de82ad3b83e 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -9,71 +11,14 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "ctrlc" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c" -dependencies = [ - "kernel32-sys", - "nix", - "winapi 0.2.8", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "libc" -version = "0.2.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" - [[package]] name = "multiple_crate_versions" version = "0.1.0" dependencies = [ "ansi_term", - "ctrlc", -] - -[[package]] -name = "nix" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "void", + "winapi 0.2.8", ] -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "winapi" version = "0.2.8" @@ -90,12 +35,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 4f97b01133402..79317659ac072 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -6,5 +6,5 @@ publish = false [workspace] [dependencies] -ctrlc = "=3.1.0" +winapi = "0.2" ansi_term = "=0.11.0" From 95b5a7bbe244eb598fe3eb0bfad9eba6dadd7172 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Wed, 24 May 2023 16:10:09 +0200 Subject: [PATCH 488/806] replace `-` instead of erroring out --- clippy_dev/src/main.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 41fdbe099aec0..99aa854bc4320 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -5,6 +5,7 @@ use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; +use std::convert::Infallible; fn main() { let matches = get_clap_config(); @@ -181,13 +182,7 @@ fn get_clap_config() -> ArgMatches { .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") .required(true) - .value_parser(|name: &str| { - if name.contains('-') { - Err("Lint name cannot contain `-`, use `_` instead.") - } else { - Ok(name.to_owned()) - } - }), + .value_parser(|name: &str| Ok::<_, Infallible>(name.replace("-", "_"))), Arg::new("category") .short('c') .long("category") From 578d99477ab4e01721372014c1fdaf3479fdf34d Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Thu, 25 May 2023 00:16:52 +1000 Subject: [PATCH 489/806] Move on-type formatting request handler onto the main thread --- crates/rust-analyzer/src/main_loop.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f06abe0763e36..7464c47a9f339 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -652,14 +652,20 @@ impl GlobalState { use crate::handlers::request as handlers; dispatcher + // Request handlers that must run on the main thread + // because they mutate GlobalState: .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) + // Request handlers which are related to the user typing + // are run on the main thread to reduce latency: .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) + .on_sync::(handlers::handle_on_type_formatting) + // All other request handlers: .on::(handlers::fetch_dependency_list) .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) @@ -680,7 +686,6 @@ impl GlobalState { .on::(handlers::handle_open_cargo_toml) .on::(handlers::handle_move_item) .on::(handlers::handle_workspace_symbol) - .on::(handlers::handle_on_type_formatting) .on::(handlers::handle_document_symbol) .on::(handlers::handle_goto_definition) .on::(handlers::handle_goto_declaration) From 430bdd3509d9e188ec4904e2b5cd23e9f3e63e61 Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Thu, 25 May 2023 00:22:14 +1000 Subject: [PATCH 490/806] Run the main thread under the User Interactive QoS class --- crates/rust-analyzer/src/bin/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 660a780eb0383..3224aeae5645b 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -78,7 +78,14 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { println!("rust-analyzer {}", rust_analyzer::version()); return Ok(()); } - with_extra_thread("LspServer", stdx::thread::QoSClass::Utility, run_server)?; + + // rust-analyzer’s “main thread” is actually a secondary thread + // with an increased stack size at the User Initiated QoS class. + // We use this QoS class because any delay in the main loop + // will make actions like hitting enter in the editor slow. + // rust-analyzer does not block the editor’s render loop, + // so we don’t use User Interactive. + with_extra_thread("LspServer", stdx::thread::QoSClass::UserInitiated, run_server)?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, From c70f2a2e50a44aa2c09b2179d6182588efb6804f Mon Sep 17 00:00:00 2001 From: Timo <30553356+y21@users.noreply.github.com> Date: Wed, 24 May 2023 16:45:18 +0200 Subject: [PATCH 491/806] apply suggestion Co-authored-by: Philipp Krones --- clippy_dev/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 99aa854bc4320..c03fbe9d275f3 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -182,7 +182,7 @@ fn get_clap_config() -> ArgMatches { .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") .required(true) - .value_parser(|name: &str| Ok::<_, Infallible>(name.replace("-", "_"))), + .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))), Arg::new("category") .short('c') .long("category") From c7ef6c25b7c2a41f0fef8b9de5827b7b074586bd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 24 May 2023 18:04:29 +0200 Subject: [PATCH 492/806] internal: Replace Display impl for `Name` --- crates/hir-def/src/body/pretty.rs | 50 +++++--- crates/hir-def/src/builtin_type.rs | 10 +- crates/hir-def/src/hir/type_ref.rs | 24 ++-- crates/hir-def/src/import_map.rs | 87 +++++++++---- crates/hir-def/src/item_scope.rs | 10 +- crates/hir-def/src/item_tree.rs | 4 +- crates/hir-def/src/item_tree/pretty.rs | 74 +++++++----- crates/hir-def/src/item_tree/tests.rs | 2 +- crates/hir-def/src/nameres.rs | 18 ++- crates/hir-def/src/nameres/collector.rs | 14 ++- crates/hir-def/src/nameres/mod_resolution.rs | 18 ++- crates/hir-def/src/nameres/path_resolution.rs | 11 +- crates/hir-def/src/nameres/tests/macros.rs | 17 ++- crates/hir-def/src/pretty.rs | 77 +++++++----- crates/hir-expand/src/mod_path.rs | 102 ++++++++++------ crates/hir-expand/src/name.rs | 60 +++++---- crates/hir-ty/src/chalk_db.rs | 16 ++- crates/hir-ty/src/consteval/tests.rs | 2 +- crates/hir-ty/src/db.rs | 16 ++- crates/hir-ty/src/diagnostics/decl_check.rs | 39 +++--- crates/hir-ty/src/diagnostics/match_check.rs | 20 ++- crates/hir-ty/src/display.rs | 92 +++++++++----- crates/hir-ty/src/infer/closure.rs | 2 +- crates/hir-ty/src/mir/eval.rs | 18 ++- crates/hir-ty/src/mir/eval/tests.rs | 2 +- crates/hir-ty/src/mir/lower.rs | 24 ++-- crates/hir-ty/src/mir/pretty.rs | 47 +++++--- crates/hir-ty/src/tls.rs | 22 ++-- crates/hir-ty/src/traits.rs | 2 +- crates/hir/src/display.rs | 57 +++++---- crates/hir/src/lib.rs | 8 +- .../src/handlers/add_missing_impl_members.rs | 3 +- .../ide-assists/src/handlers/auto_import.rs | 2 +- .../src/handlers/convert_bool_then.rs | 2 +- .../handlers/convert_iter_for_each_to_for.rs | 2 +- .../src/handlers/expand_glob_import.rs | 2 +- .../src/handlers/extract_function.rs | 16 ++- .../src/handlers/extract_module.rs | 2 +- .../extract_struct_from_enum_variant.rs | 2 +- .../src/handlers/fix_visibility.rs | 11 +- .../src/handlers/generate_delegate_methods.rs | 11 +- .../src/handlers/generate_deref.rs | 6 +- .../src/handlers/generate_function.rs | 2 +- .../src/handlers/move_const_to_impl.rs | 2 +- .../src/handlers/move_from_mod_rs.rs | 2 +- .../src/handlers/move_module_to_file.rs | 2 +- .../src/handlers/move_to_mod_rs.rs | 2 +- .../ide-assists/src/handlers/qualify_path.rs | 10 +- .../src/handlers/reorder_fields.rs | 2 +- .../src/handlers/reorder_impl_items.rs | 2 +- crates/ide-assists/src/utils/suggest_name.rs | 4 +- crates/ide-completion/src/completions.rs | 35 +++--- .../src/completions/attribute.rs | 2 +- .../src/completions/attribute/cfg.rs | 6 +- .../src/completions/attribute/derive.rs | 2 +- .../src/completions/attribute/lint.rs | 2 +- .../src/completions/attribute/repr.rs | 2 +- crates/ide-completion/src/completions/dot.rs | 2 +- .../src/completions/env_vars.rs | 2 +- .../src/completions/extern_abi.rs | 4 +- .../src/completions/flyimport.rs | 4 +- .../src/completions/fn_param.rs | 8 +- .../src/completions/format_string.rs | 2 +- .../src/completions/item_list/trait_impl.rs | 10 +- crates/ide-completion/src/completions/mod_.rs | 4 +- .../ide-completion/src/completions/postfix.rs | 47 ++++---- .../src/completions/postfix/format_like.rs | 2 +- .../ide-completion/src/completions/record.rs | 4 +- .../ide-completion/src/completions/snippet.rs | 14 +-- crates/ide-completion/src/completions/use_.rs | 6 +- crates/ide-completion/src/context/analysis.rs | 2 +- crates/ide-completion/src/item.rs | 22 +++- crates/ide-completion/src/lib.rs | 2 +- crates/ide-completion/src/render.rs | 32 +++-- crates/ide-completion/src/render/const_.rs | 2 +- crates/ide-completion/src/render/function.rs | 9 +- crates/ide-completion/src/render/literal.rs | 8 +- crates/ide-completion/src/render/pattern.rs | 9 +- .../ide-completion/src/render/type_alias.rs | 2 +- .../src/render/union_literal.rs | 20 +-- crates/ide-completion/src/render/variant.rs | 6 +- crates/ide-completion/src/tests.rs | 10 +- crates/ide-db/src/imports/import_assets.rs | 4 +- crates/ide-db/src/rename.rs | 9 +- crates/ide-db/src/traits.rs | 18 +-- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/mutability_errors.rs | 5 +- .../src/handlers/private_assoc_item.rs | 6 +- .../src/handlers/private_field.rs | 4 +- .../src/handlers/undeclared_label.rs | 2 +- .../src/handlers/unreachable_label.rs | 2 +- .../src/handlers/unresolved_field.rs | 2 +- .../src/handlers/unresolved_macro_call.rs | 2 +- .../src/handlers/unresolved_method.rs | 2 +- crates/ide-ssr/src/lib.rs | 1 + crates/ide-ssr/src/replacing.rs | 18 ++- crates/ide/src/doc_links.rs | 48 +++++--- crates/ide/src/expand_macro.rs | 2 +- crates/ide/src/hover.rs | 2 +- crates/ide/src/hover/render.rs | 24 ++-- crates/ide/src/inlay_hints/closing_brace.rs | 2 +- crates/ide/src/moniker.rs | 114 ++++++++++-------- crates/ide/src/runnables.rs | 28 +++-- crates/ide/src/signature_help.rs | 48 +++++--- crates/ide/src/view_item_tree.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 14 ++- crates/rust-analyzer/src/cli/scip.rs | 12 +- crates/rust-analyzer/src/to_proto.rs | 8 +- 108 files changed, 1043 insertions(+), 654 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index bdc84fefe6666..7390a8f1f2b18 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Write}; +use hir_expand::db::ExpandDatabase; use syntax::ast::HasName; use crate::{ @@ -18,16 +19,22 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo let header = match owner { DefWithBodyId::FunctionId(it) => { let item_tree_id = it.lookup(db).id; - format!("fn {}", item_tree_id.item_tree(db)[item_tree_id.value].name) + format!( + "fn {}", + item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) + ) } DefWithBodyId::StaticId(it) => { let item_tree_id = it.lookup(db).id; - format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name) + format!( + "static {} = ", + item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) + ) } DefWithBodyId::ConstId(it) => { let item_tree_id = it.lookup(db).id; let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { - Some(name) => name.to_string(), + Some(name) => name.display(db.upcast()).to_string(), None => "_".to_string(), }; format!("const {name} = ") @@ -42,7 +49,8 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo } }; - let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false }; + let mut p = + Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| { @@ -61,12 +69,13 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo } pub(super) fn print_expr_hir( - _db: &dyn DefDatabase, + db: &dyn DefDatabase, body: &Body, _owner: DefWithBodyId, expr: ExprId, ) -> String { - let mut p = Printer { body, buf: String::new(), indent_level: 0, needs_indent: false }; + let mut p = + Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false }; p.print_expr(expr); p.buf } @@ -87,6 +96,7 @@ macro_rules! wln { } struct Printer<'a> { + db: &'a dyn ExpandDatabase, body: &'a Body, buf: String, indent_level: usize, @@ -161,14 +171,14 @@ impl<'a> Printer<'a> { } Expr::Loop { body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name); + w!(self, "{}: ", self.body[*lbl].name.display(self.db)); } w!(self, "loop "); self.print_expr(*body); } Expr::While { condition, body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name); + w!(self, "{}: ", self.body[*lbl].name.display(self.db)); } w!(self, "while "); self.print_expr(*condition); @@ -176,7 +186,7 @@ impl<'a> Printer<'a> { } Expr::For { iterable, pat, body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name); + w!(self, "{}: ", self.body[*lbl].name.display(self.db)); } w!(self, "for "); self.print_pat(*pat); @@ -199,10 +209,10 @@ impl<'a> Printer<'a> { } Expr::MethodCall { receiver, method_name, args, generic_args } => { self.print_expr(*receiver); - w!(self, ".{}", method_name); + w!(self, ".{}", method_name.display(self.db)); if let Some(args) = generic_args { w!(self, "::<"); - print_generic_args(args, self).unwrap(); + print_generic_args(self.db, args, self).unwrap(); w!(self, ">"); } w!(self, "("); @@ -237,13 +247,13 @@ impl<'a> Printer<'a> { Expr::Continue { label } => { w!(self, "continue"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name); + w!(self, " {}", self.body[*lbl].name.display(self.db)); } } Expr::Break { expr, label } => { w!(self, "break"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name); + w!(self, " {}", self.body[*lbl].name.display(self.db)); } if let Some(expr) = expr { self.whitespace(); @@ -282,7 +292,7 @@ impl<'a> Printer<'a> { w!(self, "{{"); self.indented(|p| { for field in &**fields { - w!(p, "{}: ", field.name); + w!(p, "{}: ", field.name.display(self.db)); p.print_expr(field.expr); wln!(p, ","); } @@ -299,7 +309,7 @@ impl<'a> Printer<'a> { } Expr::Field { expr, name } => { self.print_expr(*expr); - w!(self, ".{}", name); + w!(self, ".{}", name.display(self.db)); } Expr::Await { expr } => { self.print_expr(*expr); @@ -437,7 +447,7 @@ impl<'a> Printer<'a> { } Expr::Literal(lit) => self.print_literal(lit), Expr::Block { id: _, statements, tail, label } => { - let label = label.map(|lbl| format!("{}: ", self.body[lbl].name)); + let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db))); self.print_block(label.as_deref(), statements, tail); } Expr::Unsafe { id: _, statements, tail } => { @@ -513,7 +523,7 @@ impl<'a> Printer<'a> { w!(self, " {{"); self.indented(|p| { for arg in args.iter() { - w!(p, "{}: ", arg.name); + w!(p, "{}: ", arg.name.display(self.db)); p.print_pat(arg.pat); wln!(p, ","); } @@ -646,11 +656,11 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, ty: &TypeRef) { - print_type_ref(ty, self).unwrap(); + print_type_ref(self.db, ty, self).unwrap(); } fn print_path(&mut self, path: &Path) { - print_path(path, self).unwrap(); + print_path(self.db, path, self).unwrap(); } fn print_binding(&mut self, id: BindingId) { @@ -661,6 +671,6 @@ impl<'a> Printer<'a> { BindingAnnotation::Ref => "ref ", BindingAnnotation::RefMut => "ref mut ", }; - w!(self, "{}{}", mode, name); + w!(self, "{}{}", mode, name.display(self.db)); } } diff --git a/crates/hir-def/src/builtin_type.rs b/crates/hir-def/src/builtin_type.rs index dd69c3ab47315..61b2481978e27 100644 --- a/crates/hir-def/src/builtin_type.rs +++ b/crates/hir-def/src/builtin_type.rs @@ -106,8 +106,14 @@ impl AsName for BuiltinType { impl fmt::Display for BuiltinType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let type_name = self.as_name(); - type_name.fmt(f) + match self { + BuiltinType::Char => f.write_str("char"), + BuiltinType::Bool => f.write_str("bool"), + BuiltinType::Str => f.write_str("str"), + BuiltinType::Int(it) => it.fmt(f), + BuiltinType::Uint(it) => it.fmt(f), + BuiltinType::Float(it) => it.fmt(f), + } } } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 06e6be66baadb..0573c9a6f8af4 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -1,9 +1,11 @@ //! HIR for references to types. Paths in these are not yet resolved. They can //! be directly created from an ast::TypeRef, without further queries. +use core::fmt; use std::fmt::Write; use hir_expand::{ + db::ExpandDatabase, name::{AsName, Name}, AstId, }; @@ -383,15 +385,6 @@ pub enum ConstRefOrPath { Path(Name), } -impl std::fmt::Display for ConstRefOrPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ConstRefOrPath::Scalar(s) => s.fmt(f), - ConstRefOrPath::Path(n) => n.fmt(f), - } - } -} - impl ConstRefOrPath { pub(crate) fn from_expr_opt(expr: Option) -> Self { match expr { @@ -400,6 +393,19 @@ impl ConstRefOrPath { } } + pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a { + struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRefOrPath); + impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.1 { + ConstRefOrPath::Scalar(s) => s.fmt(f), + ConstRefOrPath::Path(n) => n.display(self.0).fmt(f), + } + } + } + Display(db, self) + } + // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this // parse stage. fn from_expr(expr: ast::Expr) -> Self { diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 4ea807e7d585f..6ef2949ef51a1 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -33,13 +33,23 @@ pub struct ImportPath { pub segments: Vec, } -impl fmt::Display for ImportPath { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.segments.iter().format("::"), f) +impl ImportPath { + pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a { + struct Display<'a> { + db: &'a dyn DefDatabase, + path: &'a ImportPath, + } + impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt( + &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"), + f, + ) + } + } + Display { db, path: self } } -} -impl ImportPath { fn len(&self) -> usize { self.segments.len() } @@ -76,7 +86,7 @@ impl ImportMap { let mut importables = import_map .map .iter() - .map(|(item, info)| (item, fst_path(&info.path))) + .map(|(item, info)| (item, fst_path(db, &info.path))) .collect::>(); importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2)); @@ -113,6 +123,25 @@ impl ImportMap { self.map.get(&item) } + #[cfg(test)] + fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, info)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({ns})", info.path.display(db)) + }) + .collect(); + + importable_paths.sort(); + importable_paths.join("\n") + } + fn collect_trait_assoc_items( &mut self, db: &dyn DefDatabase, @@ -238,13 +267,10 @@ impl fmt::Debug for ImportMap { let mut importable_paths: Vec<_> = self .map .iter() - .map(|(item, info)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({ns})", info.path) + .map(|(item, _)| match item { + ItemInNs::Types(it) => format!("- {it:?} (t)",), + ItemInNs::Values(it) => format!("- {it:?} (v)",), + ItemInNs::Macros(it) => format!("- {it:?} (m)",), }) .collect(); @@ -253,9 +279,9 @@ impl fmt::Debug for ImportMap { } } -fn fst_path(path: &ImportPath) -> String { +fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { let _p = profile::span("fst_path"); - let mut s = path.to_string(); + let mut s = path.display(db).to_string(); s.make_ascii_lowercase(); s } @@ -348,7 +374,12 @@ impl Query { self } - fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { + fn import_matches( + &self, + db: &dyn DefDatabase, + import: &ImportInfo, + enforce_lowercase: bool, + ) -> bool { let _p = profile::span("import_map::Query::import_matches"); if import.is_trait_assoc_item { if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { @@ -359,9 +390,9 @@ impl Query { } let mut input = if import.is_trait_assoc_item || self.name_only { - import.path.segments.last().unwrap().to_string() + import.path.segments.last().unwrap().display(db.upcast()).to_string() } else { - import.path.to_string() + import.path.display(db).to_string() }; if enforce_lowercase || !self.case_sensitive { input.make_ascii_lowercase(); @@ -426,25 +457,27 @@ pub fn search_dependencies( let importables = &import_map.importables[indexed_value.value as usize..]; let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(common_importable_data, true) { + if !query.import_matches(db, common_importable_data, true) { continue; } // Path shared by the importable items in this group. - let common_importables_path_fst = fst_path(&common_importable_data.path); + let common_importables_path_fst = fst_path(db, &common_importable_data.path); // Add the items from this `ModPath` group. Those are all subsequent items in // `importables` whose paths match `path`. let iter = importables .iter() .copied() - .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path)) + .take_while(|item| { + common_importables_path_fst == fst_path(db, &import_map.map[item].path) + }) .filter(|&item| match item_import_kind(item) { Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), None => true, }) .filter(|item| { !query.case_sensitive // we've already checked the common importables path case-insensitively - || query.import_matches(&import_map.map[item], false) + || query.import_matches(db, &import_map.map[item], false) }); res.extend(iter); @@ -501,7 +534,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - dependency_imports.path_of(dependency)?.to_string(), + dependency_imports.path_of(dependency)?.display(&db).to_string(), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -552,7 +585,11 @@ mod tests { None } })?; - return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?)); + return Some(format!( + "{}::{}", + dependency_imports.path_of(trait_)?.display(db), + assoc_item_name.display(db.upcast()) + )); } None } @@ -592,7 +629,7 @@ mod tests { let map = db.import_map(krate); - Some(format!("{name}:\n{map:?}\n")) + Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast()))) }) .sorted() .collect::(); diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 991e447033fe7..3ed321d189d36 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -4,7 +4,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; -use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId}; +use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId}; use itertools::Itertools; use once_cell::sync::Lazy; use profile::Count; @@ -358,12 +358,16 @@ impl ItemScope { } } - pub(crate) fn dump(&self, buf: &mut String) { + pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) { let mut entries: Vec<_> = self.resolutions().collect(); entries.sort_by_key(|(name, _)| name.clone()); for (name, def) in entries { - format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string())); + format_to!( + buf, + "{}:", + name.map_or("_".to_string(), |name| name.display(db).to_string()) + ); if def.types.is_some() { buf.push_str(" t"); diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index c332cc28c3c17..590ed64af5d80 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -167,8 +167,8 @@ impl ItemTree { Attrs::filter(db, krate, self.raw_attrs(of).clone()) } - pub fn pretty_print(&self) -> String { - pretty::print_item_tree(self) + pub fn pretty_print(&self, db: &dyn DefDatabase) -> String { + pretty::print_item_tree(db.upcast(), self) } fn data(&self) -> &ItemTreeData { diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 94c5157386ef7..e873316a578c0 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -2,6 +2,8 @@ use std::fmt::{self, Write}; +use hir_expand::db::ExpandDatabase; + use crate::{ generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, pretty::{print_path, print_type_bounds, print_type_ref}, @@ -10,8 +12,8 @@ use crate::{ use super::*; -pub(super) fn print_item_tree(tree: &ItemTree) -> String { - let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; +pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String { + let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { p.print_attrs(attrs, true); @@ -43,6 +45,7 @@ macro_rules! wln { } struct Printer<'a> { + db: &'a dyn ExpandDatabase, tree: &'a ItemTree, buf: String, indent_level: usize, @@ -88,7 +91,7 @@ impl<'a> Printer<'a> { self, "#{}[{}{}]", inner, - attr.path, + attr.path.display(self.db), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), ); } @@ -102,7 +105,7 @@ impl<'a> Printer<'a> { fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { - RawVisibility::Module(path) => w!(self, "pub({}) ", path), + RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)), RawVisibility::Public => w!(self, "pub "), }; } @@ -117,7 +120,7 @@ impl<'a> Printer<'a> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); - w!(this, "{}: ", name); + w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); wln!(this, ","); } @@ -131,7 +134,7 @@ impl<'a> Printer<'a> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); - w!(this, "{}: ", name); + w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); wln!(this, ","); } @@ -164,20 +167,20 @@ impl<'a> Printer<'a> { fn print_use_tree(&mut self, use_tree: &UseTree) { match &use_tree.kind { UseTreeKind::Single { path, alias } => { - w!(self, "{}", path); + w!(self, "{}", path.display(self.db)); if let Some(alias) = alias { w!(self, " as {}", alias); } } UseTreeKind::Glob { path } => { if let Some(path) = path { - w!(self, "{}::", path); + w!(self, "{}::", path.display(self.db)); } w!(self, "*"); } UseTreeKind::Prefixed { prefix, list } => { if let Some(prefix) = prefix { - w!(self, "{}::", prefix); + w!(self, "{}::", prefix.display(self.db)); } w!(self, "{{"); for (i, tree) in list.iter().enumerate() { @@ -205,7 +208,7 @@ impl<'a> Printer<'a> { ModItem::ExternCrate(it) => { let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "extern crate {}", name); + w!(self, "extern crate {}", name.display(self.db)); if let Some(alias) = alias { w!(self, " as {}", alias); } @@ -252,7 +255,7 @@ impl<'a> Printer<'a> { if let Some(abi) = abi { w!(self, "extern \"{}\" ", abi); } - w!(self, "fn {}", name); + w!(self, "fn {}", name.display(self.db)); self.print_generic_params(explicit_generic_params); w!(self, "("); if !params.is_empty() { @@ -286,7 +289,7 @@ impl<'a> Printer<'a> { ModItem::Struct(it) => { let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "struct {}", name); + w!(self, "struct {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -298,7 +301,7 @@ impl<'a> Printer<'a> { ModItem::Union(it) => { let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "union {}", name); + w!(self, "union {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -310,14 +313,14 @@ impl<'a> Printer<'a> { ModItem::Enum(it) => { let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "enum {}", name); + w!(self, "enum {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in variants.clone() { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; this.print_attrs_of(variant); - w!(this, "{}", name); + w!(this, "{}", name.display(self.db)); this.print_fields(fields); wln!(this, ","); } @@ -329,7 +332,7 @@ impl<'a> Printer<'a> { self.print_visibility(*visibility); w!(self, "const "); match name { - Some(name) => w!(self, "{}", name), + Some(name) => w!(self, "{}", name.display(self.db)), None => w!(self, "_"), } w!(self, ": "); @@ -343,7 +346,7 @@ impl<'a> Printer<'a> { if *mutable { w!(self, "mut "); } - w!(self, "{}: ", name); + w!(self, "{}: ", name.display(self.db)); self.print_type_ref(type_ref); w!(self, " = _;"); wln!(self); @@ -365,7 +368,7 @@ impl<'a> Printer<'a> { if *is_auto { w!(self, "auto "); } - w!(self, "trait {}", name); + w!(self, "trait {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { @@ -378,7 +381,7 @@ impl<'a> Printer<'a> { ModItem::TraitAlias(it) => { let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "trait {}", name); + w!(self, "trait {}", name.display(self.db)); self.print_generic_params(generic_params); w!(self, " = "); self.print_where_clause(generic_params); @@ -411,7 +414,7 @@ impl<'a> Printer<'a> { let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "type {}", name); + w!(self, "type {}", name.display(self.db)); self.print_generic_params(generic_params); if !bounds.is_empty() { w!(self, ": "); @@ -428,7 +431,7 @@ impl<'a> Printer<'a> { ModItem::Mod(it) => { let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "mod {}", name); + w!(self, "mod {}", name.display(self.db)); match kind { ModKind::Inline { items } => { w!(self, " {{"); @@ -446,16 +449,16 @@ impl<'a> Printer<'a> { } ModItem::MacroCall(it) => { let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it]; - wln!(self, "{}!(...);", path); + wln!(self, "{}!(...);", path.display(self.db)); } ModItem::MacroRules(it) => { let MacroRules { name, ast_id: _ } = &self.tree[it]; - wln!(self, "macro_rules! {} {{ ... }}", name); + wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db)); } ModItem::MacroDef(it) => { let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - wln!(self, "macro {} {{ ... }}", name); + wln!(self, "macro {} {{ ... }}", name.display(self.db)); } } @@ -463,15 +466,15 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, type_ref: &TypeRef) { - print_type_ref(type_ref, self).unwrap(); + print_type_ref(self.db, type_ref, self).unwrap(); } fn print_type_bounds(&mut self, bounds: &[Interned]) { - print_type_bounds(bounds, self).unwrap(); + print_type_bounds(self.db, bounds, self).unwrap(); } fn print_path(&mut self, path: &Path) { - print_path(path, self).unwrap(); + print_path(self.db, path, self).unwrap(); } fn print_generic_params(&mut self, params: &GenericParams) { @@ -486,7 +489,7 @@ impl<'a> Printer<'a> { w!(self, ", "); } first = false; - w!(self, "{}", lt.name); + w!(self, "{}", lt.name.display(self.db)); } for (idx, x) in params.type_or_consts.iter() { if !first { @@ -495,11 +498,11 @@ impl<'a> Printer<'a> { first = false; match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { - Some(name) => w!(self, "{}", name), + Some(name) => w!(self, "{}", name.display(self.db)), None => w!(self, "_anon_{}", idx.into_raw()), }, TypeOrConstParamData::ConstParamData(konst) => { - w!(self, "const {}: ", konst.name); + w!(self, "const {}: ", konst.name.display(self.db)); self.print_type_ref(&konst.ty); } } @@ -531,7 +534,12 @@ impl<'a> Printer<'a> { let (target, bound) = match pred { WherePredicate::TypeBound { target, bound } => (target, bound), WherePredicate::Lifetime { target, bound } => { - wln!(this, "{}: {},", target.name, bound.name); + wln!( + this, + "{}: {},", + target.name.display(self.db), + bound.name.display(self.db) + ); continue; } WherePredicate::ForLifetime { lifetimes, target, bound } => { @@ -540,7 +548,7 @@ impl<'a> Printer<'a> { if i != 0 { w!(this, ", "); } - w!(this, "{}", lt); + w!(this, "{}", lt.display(self.db)); } w!(this, "> "); (target, bound) @@ -551,7 +559,7 @@ impl<'a> Printer<'a> { WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), WherePredicateTypeTarget::TypeOrConstParam(id) => { match ¶ms.type_or_consts[*id].name() { - Some(name) => w!(this, "{}", name), + Some(name) => w!(this, "{}", name.display(self.db)), None => w!(this, "_anon_{}", id.into_raw()), } } diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 1b7564f7a9909..5ded4b6b273ae 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB}; fn check(ra_fixture: &str, expect: Expect) { let (db, file_id) = TestDB::with_single_file(ra_fixture); let item_tree = db.file_item_tree(file_id.into()); - let pretty = item_tree.pretty_print(); + let pretty = item_tree.pretty_print(&db); expect.assert_eq(&pretty); } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 176637f9d0dde..8aceb4952a546 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -463,25 +463,31 @@ impl DefMap { let mut arc; let mut current_map = self; while let Some(block) = current_map.block { - go(&mut buf, current_map, "block scope", current_map.root); + go(&mut buf, db, current_map, "block scope", current_map.root); buf.push('\n'); arc = block.parent.def_map(db); current_map = &arc; } - go(&mut buf, current_map, "crate", current_map.root); + go(&mut buf, db, current_map, "crate", current_map.root); return buf; - fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { + fn go( + buf: &mut String, + db: &dyn DefDatabase, + map: &DefMap, + path: &str, + module: LocalModuleId, + ) { format_to!(buf, "{}\n", path); - map.modules[module].scope.dump(buf); + map.modules[module].scope.dump(db.upcast(), buf); for (name, child) in map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) { - let path = format!("{path}::{name}"); + let path = format!("{path}::{}", name.display(db.upcast())); buf.push('\n'); - go(buf, map, &path, *child); + go(buf, db, map, &path, *child); } } } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index a528d238e377b..c49bb248a7b08 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -565,7 +565,7 @@ impl DefCollector<'_> { types => { tracing::debug!( "could not resolve prelude path `{}` to module (resolved to {:?})", - path, + path.display(self.db.upcast()), types ); } @@ -766,7 +766,8 @@ impl DefCollector<'_> { } fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { - let _p = profile::span("resolve_import").detail(|| format!("{}", import.path)); + let _p = profile::span("resolve_import") + .detail(|| format!("{}", import.path.display(self.db.upcast()))); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); if import.is_extern_crate { let name = import @@ -1985,7 +1986,10 @@ impl ModCollector<'_, '_> { if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) { continue; } - tracing::debug!("non-builtin attribute {}", attr.path); + tracing::debug!( + "non-builtin attribute {}", + attr.path.display(self.def_collector.db.upcast()) + ); let ast_id = AstIdWithPath::new( self.file_id(), @@ -2119,8 +2123,8 @@ impl ModCollector<'_, '_> { stdx::always!( name == mac.name, "built-in macro {} has #[rustc_builtin_macro] which declares different name {}", - mac.name, - name + mac.name.display(self.def_collector.db.upcast()), + name.display(self.def_collector.db.upcast()) ); helpers_opt = Some(helpers); } diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs index 51c565fe12339..2dcc2c30fe169 100644 --- a/crates/hir-def/src/nameres/mod_resolution.rs +++ b/crates/hir-def/src/nameres/mod_resolution.rs @@ -74,12 +74,20 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None if file_id.is_include_macro(db.upcast()) => { - candidate_files.push(format!("{name}.rs")); - candidate_files.push(format!("{name}/mod.rs")); + candidate_files.push(format!("{}.rs", name.display(db.upcast()))); + candidate_files.push(format!("{}/mod.rs", name.display(db.upcast()))); } None => { - candidate_files.push(format!("{}{name}.rs", self.dir_path.0)); - candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0)); + candidate_files.push(format!( + "{}{}.rs", + self.dir_path.0, + name.display(db.upcast()) + )); + candidate_files.push(format!( + "{}{}/mod.rs", + self.dir_path.0, + name.display(db.upcast()) + )); } }; @@ -91,7 +99,7 @@ impl ModDir { let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { (DirPath::empty(), false) } else { - (DirPath::new(format!("{name}/")), true) + (DirPath::new(format!("{}/", name.display(db.upcast()))), true) }; if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) { return Ok((file_id, is_mod_rs, mod_dir)); diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 981171013a159..24dc4e243bf4a 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -192,8 +192,11 @@ impl DefMap { ) -> ResolvePathResult { let graph = db.crate_graph(); let _cx = stdx::panic_context::enter(format!( - "DefMap {:?} crate_name={:?} block={:?} path={path}", - self.krate, graph[self.krate].display_name, self.block + "DefMap {:?} crate_name={:?} block={:?} path={}", + self.krate, + graph[self.krate].display_name, + self.block, + path.display(db.upcast()) )); let mut segments = path.segments().iter().enumerate(); @@ -262,8 +265,8 @@ impl DefMap { ); tracing::debug!( "`super` path: {} -> {} in parent map", - path, - new_path + path.display(db.upcast()), + new_path.display(db.upcast()) ); return block.parent.def_map(db).resolve_path_fp_with_macro( db, diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 56059f7cb3bb2..57f0233607b31 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1080,7 +1080,7 @@ macro_rules! mbe { #[test] fn collects_derive_helpers() { - let def_map = compute_crate_def_map( + let db = TestDB::with_files( r#" #![crate_type="proc-macro"] struct TokenStream; @@ -1091,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { } "#, ); + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); assert_eq!(def_map.exported_derives.len(), 1); match def_map.exported_derives.values().next() { Some(helpers) => match &**helpers { - [attr] => assert_eq!(attr.to_string(), "helper_attr"), + [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"), _ => unreachable!(), }, _ => unreachable!(), @@ -1258,7 +1260,7 @@ struct A; #[test] fn macro_use_imports_all_macro_types() { - let def_map = compute_crate_def_map( + let db = TestDB::with_files( r#" //- /main.rs crate:main deps:lib #[macro_use] @@ -1281,6 +1283,8 @@ struct TokenStream; fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); let root_module = &def_map[def_map.root()].scope; assert!( @@ -1288,7 +1292,12 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "`#[macro_use]` shouldn't bring macros into textual macro scope", ); - let actual = def_map.macro_use_prelude.iter().map(|(name, _)| name).sorted().join("\n"); + let actual = def_map + .macro_use_prelude + .iter() + .map(|(name, _)| name.display(&db).to_string()) + .sorted() + .join("\n"); expect![[r#" legacy diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index 026750b62fa75..0aead6f37f73f 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; -use hir_expand::mod_path::PathKind; +use hir_expand::{db::ExpandDatabase, mod_path::PathKind}; use intern::Interned; use itertools::Itertools; @@ -11,14 +11,14 @@ use crate::{ type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, }; -pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { if let Path::LangItem(x) = path { return write!(buf, "$lang_item::{x:?}"); } match path.type_anchor() { Some(anchor) => { write!(buf, "<")?; - print_type_ref(anchor, buf)?; + print_type_ref(db, anchor, buf)?; write!(buf, ">::")?; } None => match path.kind() { @@ -44,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { write!(buf, "::")?; } - write!(buf, "{}", segment.name)?; + write!(buf, "{}", segment.name.display(db))?; if let Some(generics) = segment.args_and_bindings { write!(buf, "::<")?; - print_generic_args(generics, buf)?; + print_generic_args(db, generics, buf)?; write!(buf, ">")?; } @@ -56,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { Ok(()) } -pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_generic_args( + db: &dyn ExpandDatabase, + generics: &GenericArgs, + buf: &mut dyn Write, +) -> fmt::Result { let mut first = true; let args = if generics.has_self_type { let (self_ty, args) = generics.args.split_first().unwrap(); write!(buf, "Self=")?; - print_generic_arg(self_ty, buf)?; + print_generic_arg(db, self_ty, buf)?; first = false; args } else { @@ -72,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> write!(buf, ", ")?; } first = false; - print_generic_arg(arg, buf)?; + print_generic_arg(db, arg, buf)?; } for binding in generics.bindings.iter() { if !first { write!(buf, ", ")?; } first = false; - write!(buf, "{}", binding.name)?; + write!(buf, "{}", binding.name.display(db))?; if !binding.bounds.is_empty() { write!(buf, ": ")?; - print_type_bounds(&binding.bounds, buf)?; + print_type_bounds(db, &binding.bounds, buf)?; } if let Some(ty) = &binding.type_ref { write!(buf, " = ")?; - print_type_ref(ty, buf)?; + print_type_ref(db, ty, buf)?; } } Ok(()) } -pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_generic_arg( + db: &dyn ExpandDatabase, + arg: &GenericArg, + buf: &mut dyn Write, +) -> fmt::Result { match arg { - GenericArg::Type(ty) => print_type_ref(ty, buf), - GenericArg::Const(c) => write!(buf, "{c}"), - GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), + GenericArg::Type(ty) => print_type_ref(db, ty, buf), + GenericArg::Const(c) => write!(buf, "{}", c.display(db)), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)), } } -pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_type_ref( + db: &dyn ExpandDatabase, + type_ref: &TypeRef, + buf: &mut dyn Write, +) -> fmt::Result { // FIXME: deduplicate with `HirDisplay` impl match type_ref { TypeRef::Never => write!(buf, "!")?, @@ -111,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re if i != 0 { write!(buf, ", ")?; } - print_type_ref(field, buf)?; + print_type_ref(db, field, buf)?; } write!(buf, ")")?; } - TypeRef::Path(path) => print_path(path, buf)?, + TypeRef::Path(path) => print_path(db, path, buf)?, TypeRef::RawPtr(pointee, mtbl) => { let mtbl = match mtbl { Mutability::Shared => "*const", Mutability::Mut => "*mut", }; write!(buf, "{mtbl} ")?; - print_type_ref(pointee, buf)?; + print_type_ref(db, pointee, buf)?; } TypeRef::Reference(pointee, lt, mtbl) => { let mtbl = match mtbl { @@ -131,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re }; write!(buf, "&")?; if let Some(lt) = lt { - write!(buf, "{} ", lt.name)?; + write!(buf, "{} ", lt.name.display(db))?; } write!(buf, "{mtbl}")?; - print_type_ref(pointee, buf)?; + print_type_ref(db, pointee, buf)?; } TypeRef::Array(elem, len) => { write!(buf, "[")?; - print_type_ref(elem, buf)?; - write!(buf, "; {len}]")?; + print_type_ref(db, elem, buf)?; + write!(buf, "; {}]", len.display(db))?; } TypeRef::Slice(elem) => { write!(buf, "[")?; - print_type_ref(elem, buf)?; + print_type_ref(db, elem, buf)?; write!(buf, "]")?; } TypeRef::Fn(args_and_ret, varargs, is_unsafe) => { @@ -157,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re if i != 0 { write!(buf, ", ")?; } - print_type_ref(typeref, buf)?; + print_type_ref(db, typeref, buf)?; } if *varargs { if !args.is_empty() { @@ -166,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re write!(buf, "...")?; } write!(buf, ") -> ")?; - print_type_ref(return_type, buf)?; + print_type_ref(db, return_type, buf)?; } TypeRef::Macro(_ast_id) => { write!(buf, "")?; @@ -174,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re TypeRef::Error => write!(buf, "{{unknown}}")?, TypeRef::ImplTrait(bounds) => { write!(buf, "impl ")?; - print_type_bounds(bounds, buf)?; + print_type_bounds(db, bounds, buf)?; } TypeRef::DynTrait(bounds) => { write!(buf, "dyn ")?; - print_type_bounds(bounds, buf)?; + print_type_bounds(db, bounds, buf)?; } } @@ -186,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re } pub(crate) fn print_type_bounds( + db: &dyn ExpandDatabase, bounds: &[Interned], buf: &mut dyn Write, ) -> fmt::Result { @@ -200,13 +213,13 @@ pub(crate) fn print_type_bounds( TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(buf, "?")?, } - print_path(path, buf)?; + print_path(db, path, buf)?; } TypeBound::ForLifetime(lifetimes, path) => { - write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; - print_path(path, buf)?; + write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?; + print_path(db, path, buf)?; } - TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?, + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?, TypeBound::Error => write!(buf, "{{unknown}}")?, } } diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index e9393cc89aedf..47a8ab7de77f9 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -1,7 +1,7 @@ //! A lowering for `use`-paths (more generally, paths without angle-bracketed segments). use std::{ - fmt::{self, Display}, + fmt::{self, Display as _}, iter, }; @@ -24,6 +24,12 @@ pub struct ModPath { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct UnescapedModPath<'a>(&'a ModPath); +impl<'a> UnescapedModPath<'a> { + pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + UnescapedDisplay { db, path: self } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -110,52 +116,30 @@ impl ModPath { UnescapedModPath(self) } - fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result { - let mut first_segment = true; - let mut add_segment = |s| -> fmt::Result { - if !first_segment { - f.write_str("::")?; - } - first_segment = false; - f.write_str(s)?; - Ok(()) - }; - match self.kind { - PathKind::Plain => {} - PathKind::Super(0) => add_segment("self")?, - PathKind::Super(n) => { - for _ in 0..n { - add_segment("super")?; - } - } - PathKind::Crate => add_segment("crate")?, - PathKind::Abs => add_segment("")?, - PathKind::DollarCrate(_) => add_segment("$crate")?, - } - for segment in &self.segments { - if !first_segment { - f.write_str("::")?; - } - first_segment = false; - if escaped { - segment.fmt(f)? - } else { - segment.unescaped().fmt(f)? - }; - } - Ok(()) + pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + Display { db, path: self } } } -impl Display for ModPath { +struct Display<'a> { + db: &'a dyn ExpandDatabase, + path: &'a ModPath, +} + +impl<'a> fmt::Display for Display<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self._fmt(f, true) + display_fmt_path(self.db, self.path, f, true) } } -impl<'a> Display for UnescapedModPath<'a> { +struct UnescapedDisplay<'a> { + db: &'a dyn ExpandDatabase, + path: &'a UnescapedModPath<'a>, +} + +impl<'a> fmt::Display for UnescapedDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0._fmt(f, false) + display_fmt_path(self.db, self.path.0, f, false) } } @@ -164,6 +148,46 @@ impl From for ModPath { ModPath::from_segments(PathKind::Plain, iter::once(name)) } } +fn display_fmt_path( + db: &dyn ExpandDatabase, + path: &ModPath, + f: &mut fmt::Formatter<'_>, + escaped: bool, +) -> fmt::Result { + let mut first_segment = true; + let mut add_segment = |s| -> fmt::Result { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + f.write_str(s)?; + Ok(()) + }; + match path.kind { + PathKind::Plain => {} + PathKind::Super(0) => add_segment("self")?, + PathKind::Super(n) => { + for _ in 0..n { + add_segment("super")?; + } + } + PathKind::Crate => add_segment("crate")?, + PathKind::Abs => add_segment("")?, + PathKind::DollarCrate(_) => add_segment("$crate")?, + } + for segment in &path.segments { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + if escaped { + segment.display(db).fmt(f)?; + } else { + segment.unescaped().display(db).fmt(f)?; + } + } + Ok(()) +} fn convert_path( db: &dyn ExpandDatabase, diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 10a251ba78433..0e95673dbd439 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -24,27 +24,6 @@ enum Repr { TupleField(usize), } -impl fmt::Display for Name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - Repr::Text(text) => fmt::Display::fmt(&text, f), - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } - } -} - -impl<'a> fmt::Display for UnescapedName<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 .0 { - Repr::Text(text) => { - let text = text.strip_prefix("r#").unwrap_or(text); - fmt::Display::fmt(&text, f) - } - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } - } -} - impl<'a> UnescapedName<'a> { /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. @@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> { Repr::TupleField(it) => SmolStr::new(it.to_string()), } } + + pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + _ = db; + UnescapedDisplay { name: self } + } } impl Name { @@ -167,6 +151,40 @@ impl Name { Repr::TupleField(_) => false, } } + + pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + _ = db; + Display { name: self } + } +} + +struct Display<'a> { + name: &'a Name, +} + +impl<'a> fmt::Display for Display<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.name.0 { + Repr::Text(text) => fmt::Display::fmt(&text, f), + Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), + } + } +} + +struct UnescapedDisplay<'a> { + name: &'a UnescapedName<'a>, +} + +impl<'a> fmt::Display for UnescapedDisplay<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.name.0 .0 { + Repr::Text(text) => { + let text = text.strip_prefix("r#").unwrap_or(text); + fmt::Display::fmt(&text, f) + } + Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), + } + } } pub trait AsName { diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 983cc22212421..ac962c9e3e1f0 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -365,13 +365,19 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn trait_name(&self, trait_id: chalk_ir::TraitId) -> String { let id = from_chalk_trait_id(trait_id); - self.db.trait_data(id).name.to_string() + self.db.trait_data(id).name.display(self.db.upcast()).to_string() } fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { match adt_id { - hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), - hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), - hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(), + hir_def::AdtId::StructId(id) => { + self.db.struct_data(id).name.display(self.db.upcast()).to_string() + } + hir_def::AdtId::EnumId(id) => { + self.db.enum_data(id).name.display(self.db.upcast()).to_string() + } + hir_def::AdtId::UnionId(id) => { + self.db.union_data(id).name.display(self.db.upcast()).to_string() + } } } fn adt_size_align(&self, _id: chalk_ir::AdtId) -> Arc { @@ -380,7 +386,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { } fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { let id = self.db.associated_ty_data(assoc_ty_id).name; - self.db.type_alias_data(id).name.to_string() + self.db.type_alias_data(id).name.display(self.db.upcast()).to_string() } fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { format!("Opaque_{}", opaque_ty_id.0) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index b336fa5f0f002..6ff8e2de67de5 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -79,7 +79,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result { .declarations() .find_map(|x| match x { hir_def::ModuleDefId::ConstId(x) => { - if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { + if db.const_data(x).name.as_ref()?.display(db).to_string() == "GOAL" { Some(x) } else { None diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 5be1d0b1fd68d..3aa43dedcd571 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -243,13 +243,19 @@ pub trait HirDatabase: DefDatabase + Upcast { fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { let _p = profile::span("infer:wait").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::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::StaticId(it) => { + db.static_data(it).name.clone().display(db.upcast()).to_string() } + DefWithBodyId::ConstId(it) => db + .const_data(it) + .name + .clone() + .unwrap_or_else(Name::missing) + .display(db.upcast()) + .to_string(), DefWithBodyId::VariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].name.to_string() + db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() } }); db.infer_query(def) diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index b3a699e2d1b34..1233469b947b4 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> { } // Check the function name. - let function_name = data.name.to_string(); + let function_name = data.name.display(self.db.upcast()).to_string(); let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement { current_name: data.name.clone(), suggested_text: new_name, @@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> { id, Replacement { current_name: bind_name.clone(), - suggested_text: to_lower_snake_case(&bind_name.to_string())?, + suggested_text: to_lower_snake_case( + &bind_name.display(self.db.upcast()).to_string(), + )?, expected_case: CaseType::LowerSnakeCase, }, )) @@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Function, ident: AstPtr::new(&ast_ptr), expected_case: fn_name_replacement.expected_case, - ident_text: fn_name_replacement.current_name.to_string(), + ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: fn_name_replacement.suggested_text, }; @@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> { ident_type, ident: AstPtr::new(&name_ast), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement + .current_name + .display(self.db.upcast()) + .to_string(), suggested_text: replacement.suggested_text, }; @@ -362,7 +367,7 @@ impl<'a> DeclValidator<'a> { let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false); // Check the structure name. - let struct_name = data.name.to_string(); + let struct_name = data.name.display(self.db.upcast()).to_string(); let struct_name_replacement = if !non_camel_case_allowed { to_camel_case(&struct_name).map(|new_name| Replacement { current_name: data.name.clone(), @@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> { if !non_snake_case_allowed { if let VariantData::Record(fields) = data.variant_data.as_ref() { for (_, field) in fields.iter() { - let field_name = field.name.to_string(); + let field_name = field.name.display(self.db.upcast()).to_string(); if let Some(new_name) = to_lower_snake_case(&field_name) { let replacement = Replacement { current_name: field.name.clone(), @@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Structure, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Field, ident: AstPtr::new(&ast_ptr), expected_case: field_to_rename.expected_case, - ident_text: field_to_rename.current_name.to_string(), + ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(), suggested_text: field_to_rename.suggested_text, }; @@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> { } // Check the enum name. - let enum_name = data.name.to_string(); + let enum_name = data.name.display(self.db.upcast()).to_string(); let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement { current_name: data.name.clone(), suggested_text: new_name, @@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> { .filter_map(|(_, variant)| { Some(Replacement { current_name: variant.name.clone(), - suggested_text: to_camel_case(&variant.name.to_string())?, + suggested_text: to_camel_case( + &variant.name.display(self.db.upcast()).to_string(), + )?, expected_case: CaseType::UpperCamelCase, }) }) @@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Enum, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Variant, ident: AstPtr::new(&ast_ptr), expected_case: variant_to_rename.expected_case, - ident_text: variant_to_rename.current_name.to_string(), + ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(), suggested_text: variant_to_rename.suggested_text, }; @@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> { None => return, }; - let const_name = name.to_string(); + let const_name = name.display(self.db.upcast()).to_string(); let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) { Replacement { current_name: name.clone(), @@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Constant, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> { let name = &data.name; - let static_name = name.to_string(); + let static_name = name.display(self.db.upcast()).to_string(); let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) { Replacement { current_name: name.clone(), @@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::StaticVariable, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index 2b78e7e14fd86..f8cdeaa5e3549 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -153,7 +153,7 @@ impl<'a> PatCtxt<'a> { match (bm, ty.kind(Interner)) { (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty, (BindingMode::Ref(_), _) => { - never!("`ref {}` has wrong type {:?}", name, ty); + never!("`ref {}` has wrong type {:?}", name.display(self.db.upcast()), ty); self.errors.push(PatternError::UnexpectedType); return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; } @@ -298,7 +298,7 @@ impl HirDisplay for Pat { match &*self.kind { PatKind::Wild => write!(f, "_"), PatKind::Binding { name, subpattern } => { - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; if let Some(subpattern) = subpattern { write!(f, " @ ")?; subpattern.hir_fmt(f)?; @@ -319,10 +319,14 @@ impl HirDisplay for Pat { match variant { VariantId::EnumVariantId(v) => { let data = f.db.enum_data(v.parent); - write!(f, "{}", data.variants[v.local_id].name)?; + write!(f, "{}", data.variants[v.local_id].name.display(f.db.upcast()))?; + } + VariantId::StructId(s) => { + write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))? + } + VariantId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))? } - VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?, - VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?, }; let variant_data = variant.variant_data(f.db.upcast()); @@ -336,7 +340,11 @@ impl HirDisplay for Pat { .map(|p| { printed += 1; WriteWith(move |f| { - write!(f, "{}: ", rec_fields[p.field].name)?; + write!( + f, + "{}: ", + rec_fields[p.field].name.display(f.db.upcast()) + )?; p.pattern.hir_fmt(f) }) }); diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 39e3de6717ea3..3a34485adc44d 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -372,7 +372,13 @@ impl HirDisplay for ProjectionTy { let trait_ref = self.trait_ref(f.db); write!(f, "<")?; fmt_trait_ref(f, &trait_ref, true)?; - write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; + write!( + f, + ">::{}", + f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)) + .name + .display(f.db.upcast()) + )?; let proj_params_count = self.substitution.len(Interner) - trait_ref.substitution.len(Interner); let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; @@ -415,7 +421,8 @@ impl HirDisplay for Const { let id = from_placeholder_idx(f.db, *idx); let generics = generics(f.db.upcast(), id.parent); let param_data = &generics.params.type_or_consts[id.local_id]; - write!(f, "{}", param_data.name().unwrap()) + write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?; + Ok(()) } ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty), @@ -546,19 +553,19 @@ fn render_const_scalar( }; let mut it = fields.iter(); if matches!(data.variant_data.as_ref(), VariantData::Record(_)) { - write!(f, "{} {{", data.name)?; + write!(f, "{} {{", data.name.display(f.db.upcast()))?; if let Some((id, data)) = it.next() { - write!(f, " {}: ", data.name)?; + write!(f, " {}: ", data.name.display(f.db.upcast()))?; render_field(f, id)?; } for (id, data) in it { - write!(f, ", {}: ", data.name)?; + write!(f, ", {}: ", data.name.display(f.db.upcast()))?; render_field(f, id)?; } write!(f, " }}")?; } else { let mut it = it.map(|x| x.0); - write!(f, "{}(", data.name)?; + write!(f, "{}(", data.name.display(f.db.upcast()))?; if let Some(id) = it.next() { render_field(f, id)?; } @@ -570,10 +577,12 @@ fn render_const_scalar( } return Ok(()); } - VariantData::Unit => write!(f, "{}", data.name), + VariantData::Unit => write!(f, "{}", data.name.display(f.db.upcast())), } } - hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name), + hir_def::AdtId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) + } hir_def::AdtId::EnumId(_) => f.write_str(""), }, chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f), @@ -739,11 +748,17 @@ impl HirDisplay for Ty { let sig = db.callable_item_signature(def).substitute(Interner, parameters); f.start_location_link(def.into()); match def { - CallableDefId::FunctionId(ff) => write!(f, "fn {}", db.function_data(ff).name)?, - CallableDefId::StructId(s) => write!(f, "{}", db.struct_data(s).name)?, - CallableDefId::EnumVariantId(e) => { - write!(f, "{}", db.enum_data(e.parent).variants[e.local_id].name)? + CallableDefId::FunctionId(ff) => { + write!(f, "fn {}", db.function_data(ff).name.display(f.db.upcast()))? + } + CallableDefId::StructId(s) => { + write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))? } + CallableDefId::EnumVariantId(e) => write!( + f, + "{}", + db.enum_data(e.parent).variants[e.local_id].name.display(f.db.upcast()) + )?, }; f.end_location_link(); if parameters.len(Interner) > 0 { @@ -783,7 +798,7 @@ impl HirDisplay for Ty { hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(), hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(), }; - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; } DisplayTarget::SourceCode { module_id, allow_opaque: _ } => { if let Some(path) = find_path::find_path( @@ -792,7 +807,7 @@ impl HirDisplay for Ty { module_id, false, ) { - write!(f, "{path}")?; + write!(f, "{}", path.display(f.db.upcast()))?; } else { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::PathNotFound, @@ -818,12 +833,12 @@ impl HirDisplay for Ty { // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) if f.display_target.is_test() { f.start_location_link(trait_.into()); - write!(f, "{}", trait_data.name)?; + write!(f, "{}", trait_data.name.display(f.db.upcast()))?; f.end_location_link(); write!(f, "::")?; f.start_location_link(type_alias.into()); - write!(f, "{}", type_alias_data.name)?; + write!(f, "{}", type_alias_data.name.display(f.db.upcast()))?; f.end_location_link(); // Note that the generic args for the associated type come before those for the // trait (including the self type). @@ -846,7 +861,7 @@ impl HirDisplay for Ty { let alias = from_foreign_def_id(*type_alias); let type_alias = db.type_alias_data(alias); f.start_location_link(alias.into()); - write!(f, "{}", type_alias.name)?; + write!(f, "{}", type_alias.name.display(f.db.upcast()))?; f.end_location_link(); } TyKind::OpaqueType(opaque_ty_id, parameters) => { @@ -954,7 +969,11 @@ impl HirDisplay for Ty { match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { - write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))? + write!( + f, + "{}", + p.name.clone().unwrap_or_else(Name::missing).display(f.db.upcast()) + )? } TypeParamProvenance::ArgumentImplTrait => { let substs = generics.placeholder_subst(db); @@ -983,7 +1002,7 @@ impl HirDisplay for Ty { } }, TypeOrConstParamData::ConstParamData(p) => { - write!(f, "{}", p.name)?; + write!(f, "{}", p.name.display(f.db.upcast()))?; } } } @@ -1258,7 +1277,7 @@ fn write_bounds_like_dyn_trait( // existential) here, which is the only thing that's // possible in actual Rust, and hence don't print it f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name)?; + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; f.end_location_link(); if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) { if is_fn_trait { @@ -1297,7 +1316,7 @@ fn write_bounds_like_dyn_trait( let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id); let type_alias = f.db.type_alias_data(assoc_ty_id); f.start_location_link(assoc_ty_id.into()); - write!(f, "{}", type_alias.name)?; + write!(f, "{}", type_alias.name.display(f.db.upcast()))?; f.end_location_link(); let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self(); @@ -1364,7 +1383,7 @@ fn fmt_trait_ref( } let trait_ = tr.hir_trait_id(); f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name)?; + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; f.end_location_link(); if tr.substitution.len(Interner) > 1 { write!(f, "<")?; @@ -1394,7 +1413,7 @@ impl HirDisplay for WhereClause { write!(f, ">::",)?; let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); f.start_location_link(type_alias.into()); - write!(f, "{}", f.db.type_alias_data(type_alias).name,)?; + write!(f, "{}", f.db.type_alias_data(type_alias).name.display(f.db.upcast()),)?; f.end_location_link(); write!(f, " = ")?; ty.hir_fmt(f)?; @@ -1432,7 +1451,8 @@ impl HirDisplay for LifetimeData { let id = lt_from_placeholder_idx(f.db, *idx); let generics = generics(f.db.upcast(), id.parent); let param_data = &generics.params.lifetimes[id.local_id]; - write!(f, "{}", param_data.name) + write!(f, "{}", param_data.name.display(f.db.upcast()))?; + Ok(()) } LifetimeData::Static => write!(f, "'static"), LifetimeData::Erased => Ok(()), @@ -1508,7 +1528,7 @@ impl HirDisplay for TypeRef { }; write!(f, "&")?; if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name)?; + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; } write!(f, "{mutability}")?; inner.hir_fmt(f)?; @@ -1516,7 +1536,7 @@ impl HirDisplay for TypeRef { TypeRef::Array(inner, len) => { write!(f, "[")?; inner.hir_fmt(f)?; - write!(f, "; {len}]")?; + write!(f, "; {}]", len.display(f.db.upcast()))?; } TypeRef::Slice(inner) => { write!(f, "[")?; @@ -1533,7 +1553,7 @@ impl HirDisplay for TypeRef { for index in 0..function_parameters.len() { let (param_name, param_type) = &function_parameters[index]; if let Some(name) = param_name { - write!(f, "{name}: ")?; + write!(f, "{}: ", name.display(f.db.upcast()))?; } param_type.hir_fmt(f)?; @@ -1594,9 +1614,13 @@ impl HirDisplay for TypeBound { } path.hir_fmt(f) } - TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name), + TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name.display(f.db.upcast())), TypeBound::ForLifetime(lifetimes, path) => { - write!(f, "for<{}> ", lifetimes.iter().format(", "))?; + write!( + f, + "for<{}> ", + lifetimes.iter().map(|it| it.display(f.db.upcast())).format(", ") + )?; path.hir_fmt(f) } TypeBound::Error => write!(f, "{{error}}"), @@ -1642,7 +1666,7 @@ impl HirDisplay for Path { if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 { write!(f, "::")?; } - write!(f, "{}", segment.name)?; + write!(f, "{}", segment.name.display(f.db.upcast()))?; if let Some(generic_args) = segment.args_and_bindings { // We should be in type context, so format as `Foo` instead of `Foo::`. // Do we actually format expressions? @@ -1689,7 +1713,7 @@ impl HirDisplay for Path { } else { write!(f, ", ")?; } - write!(f, "{}", binding.name)?; + write!(f, "{}", binding.name.display(f.db.upcast()))?; match &binding.type_ref { Some(ty) => { write!(f, " = ")?; @@ -1712,8 +1736,10 @@ impl HirDisplay for hir_def::path::GenericArg { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f), - hir_def::path::GenericArg::Const(c) => write!(f, "{c}"), - hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name), + hir_def::path::GenericArg::Const(c) => write!(f, "{}", c.display(f.db.upcast())), + hir_def::path::GenericArg::Lifetime(lifetime) => { + write!(f, "{}", lifetime.name.display(f.db.upcast())) + } } } } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index a7d9246b7cff8..7878ebcc58663 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -181,7 +181,7 @@ impl CapturedItem { pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.to_string(); + let mut result = body[self.place.local].name.display(db.upcast()).to_string(); let mut field_need_paren = false; for proj in &self.place.projections { match proj { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index e4acd10aa9bea..a1f69b1f28667 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -309,7 +309,12 @@ impl MirEvalError { match func { Either::Left(func) => { let function_name = db.function_data(*func); - writeln!(f, "In function {} ({:?})", function_name.name, func)?; + writeln!( + f, + "In function {} ({:?})", + function_name.name.display(db.upcast()), + func + )?; } Either::Right(clos) => { writeln!(f, "In {:?}", clos)?; @@ -349,7 +354,7 @@ impl MirEvalError { writeln!( f, "Generic arg not provided for {}", - param.name().unwrap_or(&Name::missing()) + param.name().unwrap_or(&Name::missing()).display(db.upcast()) )?; writeln!(f, "Provided args: [")?; for g in subst.iter(Interner) { @@ -362,7 +367,8 @@ impl MirEvalError { writeln!( f, "MIR lowering for function `{}` ({:?}) failed due:", - function_name.name, func + function_name.name.display(db.upcast()), + func )?; err.pretty_print(f, db, span_formatter)?; } @@ -2070,7 +2076,11 @@ impl Evaluator<'_> { Ok(r) => Ok(r), Err(e) => { let data = self.db.enum_data(variant.parent); - let name = format!("{}::{}", data.name, data.variants[variant.local_id].name); + let name = format!( + "{}::{}", + data.name.display(self.db.upcast()), + data.variants[variant.local_id].name.display(self.db.upcast()) + ); Err(MirEvalError::ConstEvalError(name, Box::new(e))) } } diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 0685159f1c1b3..6a9d3c5b4f3f7 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -13,7 +13,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr .declarations() .find_map(|x| match x { hir_def::ModuleDefId::FunctionId(x) => { - if db.function_data(x).name.to_string() == "main" { + if db.function_data(x).name.display(db).to_string() == "main" { Some(x) } else { None diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index f73b25d83f4e1..b77e2c1c80193 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -619,7 +619,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::MethodCall { receiver, args, method_name, .. } => { let (func_id, generic_args) = - self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(format!("{}", method_name)))?; + self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?; let func = Operand::from_fn(self.db, func_id, generic_args); self.lower_call_and_args( func, @@ -1614,7 +1614,11 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(r) => Ok(r), Err(e) => { let data = self.db.enum_data(variant.parent); - let name = format!("{}::{}", data.name, data.variants[variant.local_id].name); + let name = format!( + "{}::{}", + data.name.display(self.db.upcast()), + data.variants[variant.local_id].name.display(self.db.upcast()) + ); Err(MirLowerError::ConstEvalError(name, Box::new(e))) } } @@ -1766,13 +1770,17 @@ pub fn mir_body_for_closure_query( 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::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::ConstId(it) => db + .const_data(it) + .name + .clone() + .unwrap_or_else(Name::missing) + .display(db.upcast()) + .to_string(), DefWithBodyId::VariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].name.to_string() + db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() } }); let body = db.body(def); diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 257860968c46b..3821e6a59c2d7 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -42,19 +42,23 @@ impl MirBody { ctx.for_body(|this| match ctx.body.owner { hir_def::DefWithBodyId::FunctionId(id) => { let data = db.function_data(id); - w!(this, "fn {}() ", data.name); + w!(this, "fn {}() ", data.name.display(db.upcast())); } hir_def::DefWithBodyId::StaticId(id) => { let data = db.static_data(id); - w!(this, "static {}: _ = ", data.name); + w!(this, "static {}: _ = ", data.name.display(db.upcast())); } hir_def::DefWithBodyId::ConstId(id) => { let data = db.const_data(id); - w!(this, "const {}: _ = ", data.name.as_ref().unwrap_or(&Name::missing())); + w!( + this, + "const {}: _ = ", + data.name.as_ref().unwrap_or(&Name::missing()).display(db.upcast()) + ); } hir_def::DefWithBodyId::VariantId(id) => { let data = db.enum_data(id.parent); - w!(this, "enum {} = ", data.name); + w!(this, "enum {} = ", data.name.display(db.upcast())); } }); ctx.result @@ -99,11 +103,16 @@ enum LocalName { Binding(Name, LocalId), } -impl Display for LocalName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl HirDisplay for LocalName { + fn hir_fmt( + &self, + f: &mut crate::display::HirFormatter<'_>, + ) -> Result<(), crate::display::HirDisplayError> { match self { LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())), - LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())), + LocalName::Binding(n, l) => { + write!(f, "{}_{}", n.display(f.db.upcast()), u32::from(l.into_raw())) + } } } } @@ -177,7 +186,12 @@ impl<'a> MirPrettyCtx<'a> { fn locals(&mut self) { for (id, local) in self.body.locals.iter() { - wln!(self, "let {}: {};", self.local_name(id), self.hir_display(&local.ty)); + wln!( + self, + "let {}: {};", + self.local_name(id).display(self.db), + self.hir_display(&local.ty) + ); } } @@ -206,10 +220,10 @@ impl<'a> MirPrettyCtx<'a> { wln!(this, ";"); } StatementKind::StorageDead(p) => { - wln!(this, "StorageDead({})", this.local_name(*p)); + wln!(this, "StorageDead({})", this.local_name(*p).display(self.db)); } StatementKind::StorageLive(p) => { - wln!(this, "StorageLive({})", this.local_name(*p)); + wln!(this, "StorageLive({})", this.local_name(*p).display(self.db)); } StatementKind::Deinit(p) => { w!(this, "Deinit("); @@ -267,7 +281,7 @@ impl<'a> MirPrettyCtx<'a> { 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)); + w!(this, "{}", this.local_name(local).display(this.db)); return; }; match last { @@ -285,11 +299,16 @@ impl<'a> MirPrettyCtx<'a> { f(this, local, head); let variant_name = &this.db.enum_data(e.parent).variants[e.local_id].name; - w!(this, " as {}).{}", variant_name, name); + w!( + this, + " as {}).{}", + variant_name.display(this.db.upcast()), + name.display(this.db.upcast()) + ); } hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => { f(this, local, head); - w!(this, ".{name}"); + w!(this, ".{}", name.display(this.db.upcast())); } } } @@ -299,7 +318,7 @@ impl<'a> MirPrettyCtx<'a> { } ProjectionElem::Index(l) => { f(this, local, head); - w!(this, "[{}]", this.local_name(*l)); + w!(this, "[{}]", this.local_name(*l).display(this.db)); } x => { f(this, local, head); diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs index b7e6ee6740be7..83814ed0ec1f1 100644 --- a/crates/hir-ty/src/tls.rs +++ b/crates/hir-ty/src/tls.rs @@ -24,7 +24,8 @@ impl DebugContext<'_> { AdtId::UnionId(it) => self.0.union_data(it).name.clone(), AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), }; - name.fmt(f) + name.display(self.0.upcast()).fmt(f)?; + Ok(()) } pub(crate) fn debug_trait_id( @@ -34,7 +35,8 @@ impl DebugContext<'_> { ) -> Result<(), fmt::Error> { let trait_: hir_def::TraitId = from_chalk_trait_id(id); let trait_data = self.0.trait_data(trait_); - trait_data.name.fmt(f) + trait_data.name.display(self.0.upcast()).fmt(f)?; + Ok(()) } pub(crate) fn debug_assoc_type_id( @@ -49,7 +51,13 @@ impl DebugContext<'_> { _ => panic!("associated type not in trait"), }; let trait_data = self.0.trait_data(trait_); - write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) + write!( + fmt, + "{}::{}", + trait_data.name.display(self.0.upcast()), + type_alias_data.name.display(self.0.upcast()) + )?; + Ok(()) } pub(crate) fn debug_projection_ty( @@ -67,7 +75,7 @@ impl DebugContext<'_> { let trait_ref = projection_ty.trait_ref(self.0); let trait_params = trait_ref.substitution.as_slice(Interner); let self_ty = trait_ref.self_type_parameter(Interner); - write!(fmt, "<{self_ty:?} as {trait_name}")?; + write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?; if trait_params.len() > 1 { write!( fmt, @@ -75,7 +83,7 @@ impl DebugContext<'_> { trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), )?; } - write!(fmt, ">::{}", type_alias_data.name)?; + write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?; let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len(); let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count]; @@ -105,9 +113,9 @@ impl DebugContext<'_> { } }; match def { - CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"), + CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())), CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { - write!(fmt, "{{ctor {name}}}") + write!(fmt, "{{ctor {}}}", name.display(self.0.upcast())) } } } diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 2808fe64351f4..d2ca6e5ed7c8f 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -88,7 +88,7 @@ pub(crate) fn trait_solve_query( ) -> Option { let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { - db.trait_data(it.hir_trait_id()).name.to_string() + db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string() } GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(), _ => "??".to_string(), diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 49165ca70ec45..0c8793c6df031 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -51,7 +51,7 @@ impl HirDisplay for Function { // FIXME: String escape? write!(f, "extern \"{}\" ", &**abi)?; } - write!(f, "fn {}", data.name)?; + write!(f, "fn {}", data.name.display(f.db.upcast()))?; write_generic_params(GenericDefId::FunctionId(self.id), f)?; @@ -63,7 +63,7 @@ impl HirDisplay for Function { { f.write_char('&')?; if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name)?; + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; } if let hir_def::type_ref::Mutability::Mut = mut_ { f.write_str("mut ")?; @@ -90,7 +90,7 @@ impl HirDisplay for Function { } } match local { - Some(name) => write!(f, "{name}: ")?, + Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, None => f.write_str("_: ")?, } type_ref.hir_fmt(f)?; @@ -151,7 +151,7 @@ impl HirDisplay for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("struct ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -163,7 +163,7 @@ impl HirDisplay for Enum { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("enum ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -175,7 +175,7 @@ impl HirDisplay for Union { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("union ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -186,14 +186,14 @@ impl HirDisplay for Union { impl HirDisplay for Field { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?; - write!(f, "{}: ", self.name(f.db))?; + write!(f, "{}: ", self.name(f.db).display(f.db.upcast()))?; self.ty(f.db).hir_fmt(f) } } impl HirDisplay for Variant { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let data = self.variant_data(f.db); match &*data { VariantData::Unit => {} @@ -222,7 +222,7 @@ impl HirDisplay for Variant { f.write_str(", ")?; } // Enum variant fields must be pub. - write!(f, "{}: ", field.name)?; + write!(f, "{}: ", field.name.display(f.db.upcast()))?; field.type_ref.hir_fmt(f)?; } f.write_str(" }")?; @@ -259,7 +259,7 @@ impl HirDisplay for TypeOrConstParam { impl HirDisplay for TypeParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; if f.omit_verbose_types() { return Ok(()); } @@ -286,13 +286,13 @@ impl HirDisplay for TypeParam { impl HirDisplay for LifetimeParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db)) + write!(f, "{}", self.name(f.db).display(f.db.upcast())) } } impl HirDisplay for ConstParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "const {}: ", self.name(f.db))?; + write!(f, "const {}: ", self.name(f.db).display(f.db.upcast()))?; self.ty(f.db).hir_fmt(f) } } @@ -325,7 +325,7 @@ fn write_generic_params( }; for (_, lifetime) in params.lifetimes.iter() { delim(f)?; - write!(f, "{}", lifetime.name)?; + write!(f, "{}", lifetime.name.display(f.db.upcast()))?; } for (_, ty) in params.type_or_consts.iter() { if let Some(name) = &ty.name() { @@ -335,7 +335,7 @@ fn write_generic_params( continue; } delim(f)?; - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; if let Some(default) = &ty.default { f.write_str(" = ")?; default.hir_fmt(f)?; @@ -343,7 +343,7 @@ fn write_generic_params( } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; - write!(f, "const {name}: ")?; + write!(f, "const {}: ", name.display(f.db.upcast()))?; c.ty.hir_fmt(f)?; } } @@ -380,7 +380,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f), WherePredicateTypeTarget::TypeOrConstParam(id) => { match ¶ms.type_or_consts[*id].name() { - Some(name) => write!(f, "{name}"), + Some(name) => write!(f, "{}", name.display(f.db.upcast())), None => f.write_str("{unnamed}"), } } @@ -412,10 +412,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), WherePredicate::Lifetime { target, bound } => { if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target) { - write!(f, " + {}", bound.name)?; + write!(f, " + {}", bound.name.display(f.db.upcast()))?; } else { new_predicate(f)?; - write!(f, "{}: {}", target.name, bound.name)?; + write!( + f, + "{}: {}", + target.name.display(f.db.upcast()), + bound.name.display(f.db.upcast()) + )?; } } WherePredicate::ForLifetime { lifetimes, target, bound } => { @@ -432,7 +437,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), if idx != 0 { f.write_str(", ")?; } - write!(f, "{lifetime}")?; + write!(f, "{}", lifetime.display(f.db.upcast()))?; } f.write_str("> ")?; write_target(target, f)?; @@ -462,7 +467,7 @@ impl HirDisplay for Const { let data = db.const_data(self.id); f.write_str("const ")?; match &data.name { - Some(name) => write!(f, "{name}: ")?, + Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, None => f.write_str("_: ")?, } data.type_ref.hir_fmt(f)?; @@ -478,7 +483,7 @@ impl HirDisplay for Static { if data.mutable { f.write_str("mut ")?; } - write!(f, "{}: ", &data.name)?; + write!(f, "{}: ", data.name.display(f.db.upcast()))?; data.type_ref.hir_fmt(f)?; Ok(()) } @@ -494,7 +499,7 @@ impl HirDisplay for Trait { if data.is_auto { f.write_str("auto ")?; } - write!(f, "trait {}", data.name)?; + write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitId(self.id); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -506,7 +511,7 @@ 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)?; + write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitAliasId(self.id); write_generic_params(def_id, f)?; f.write_str(" = ")?; @@ -522,7 +527,7 @@ 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)?; let data = f.db.type_alias_data(self.id); - write!(f, "type {}", data.name)?; + write!(f, "type {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TypeAliasId(self.id); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -542,7 +547,7 @@ impl HirDisplay for Module { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { // FIXME: Module doesn't have visibility saved in data. match self.name(f.db) { - Some(name) => write!(f, "mod {name}"), + Some(name) => write!(f, "mod {}", name.display(f.db.upcast())), None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) { Some(name) => write!(f, "extern crate {name}"), None => f.write_str("extern crate {unknown}"), @@ -559,6 +564,6 @@ impl HirDisplay for Macro { hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"), hir_def::MacroId::ProcMacroId(_) => f.write_str("proc_macro"), }?; - write!(f, " {}", self.name(f.db)) + write!(f, " {}", self.name(f.db).display(f.db.upcast())) } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8fac7fcd874ab..64f97452769d2 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -329,7 +329,7 @@ impl ModuleDef { segments.extend(m.name(db)) } segments.reverse(); - Some(segments.into_iter().join("::")) + Some(segments.iter().map(|it| it.display(db.upcast())).join("::")) } pub fn canonical_module_path( @@ -555,7 +555,11 @@ impl Module { /// Fills `acc` with the module's diagnostics. pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let _p = profile::span("Module::diagnostics").detail(|| { - format!("{:?}", self.name(db).map_or("".into(), |name| name.to_string())) + format!( + "{:?}", + self.name(db) + .map_or("".into(), |name| name.display(db.upcast()).to_string()) + ) }); let def_map = self.id.def_map(db.upcast()); for diag in def_map.diagnostics() { diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 9e4e6c1daf777..a35a62b8b3d3b 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -184,7 +184,8 @@ fn try_gen_trait_body( trait_ref: hir::TraitRef, impl_def: &ast::Impl, ) -> Option<()> { - let trait_path = make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).to_string()); + let trait_path = + make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db()).to_string()); let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?; let adt = hir_ty.as_adt()?.source(ctx.db())?; gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref)) diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index 17ffb861fdd9d..7acf2ea0a0de6 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -132,7 +132,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< acc.add_group( &group_label, AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{import_path}`"), + format!("Import `{}`", import_path.display(ctx.db())), range, |builder| { let scope = match scope.clone() { diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs index db96ad33047fb..1af52c592183d 100644 --- a/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -160,7 +160,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if func.name(ctx.sema.db).to_string() != "then" { + if func.name(ctx.sema.db).display(ctx.db()).to_string() != "then" { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 9e1d9a702a15f..db96c8fe40aa5 100644 --- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -119,7 +119,7 @@ pub(crate) fn convert_for_loop_with_for_each( { // We have either "for x in &col" and col implements a method called iter // or "for x in &mut col" and col implements a method called iter_mut - format_to!(buf, "{expr_behind_ref}.{method}()"); + format_to!(buf, "{expr_behind_ref}.{}()", method.display(ctx.db())); } else if let ast::Expr::RangeExpr(..) = iterable { // range expressions need to be parenthesized for the syntax to be correct format_to!(buf, "({iterable})"); diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 87f5018fb6958..5c435dd9c2960 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -65,7 +65,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); let expanded = make::use_tree_list(names_to_import.iter().map(|n| { - let path = make::ext::ident_path(&n.to_string()); + let path = make::ext::ident_path(&n.display(ctx.db()).to_string()); make::use_tree(path, None, None, false) })) .clone_for_update(); diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index c29721b464878..62cc7488298ad 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -183,7 +183,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef { let mut names_in_scope = vec![]; - semantics_scope.process_all_names(&mut |name, _| names_in_scope.push(name.to_string())); + semantics_scope.process_all_names(&mut |name, _| { + names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string()) + }); let default_name = "fun_name"; @@ -443,7 +445,7 @@ impl Param { } fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param { - let var = self.var.name(ctx.db()).to_string(); + let var = self.var.name(ctx.db()).display(ctx.db()).to_string(); let var_name = make::name(&var); let pat = match self.kind() { ParamKind::MutValue => make::ident_pat(false, true, var_name), @@ -473,7 +475,8 @@ impl TryKind { let name = adt.name(ctx.db()); // FIXME: use lang items to determine if it is std type or user defined // E.g. if user happens to define type named `Option`, we would have false positive - match name.to_string().as_str() { + let name = &name.display(ctx.db()).to_string(); + match name.as_str() { "Option" => Some(TryKind::Option), "Result" => Some(TryKind::Result { ty }), _ => None, @@ -1341,14 +1344,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St [var] => { let modifier = mut_modifier(var); let name = var.local.name(ctx.db()); - format_to!(buf, "let {modifier}{name} = ") + format_to!(buf, "let {modifier}{} = ", name.display(ctx.db())) } vars => { buf.push_str("let ("); let bindings = vars.iter().format_with(", ", |local, f| { let modifier = mut_modifier(local); let name = local.local.name(ctx.db()); - f(&format_args!("{modifier}{name}")) + f(&format_args!("{modifier}{}", name.display(ctx.db())))?; + Ok(()) }); format_to!(buf, "{bindings}"); buf.push_str(") = "); @@ -1487,7 +1491,7 @@ impl FlowHandler { } fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr { - let name = var.name(ctx.db()).to_string(); + let name = var.name(ctx.db()).display(ctx.db()).to_string(); make::expr_path(make::ext::ident_path(&name)) } diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index 795691d73ed02..de37f5f130fca 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -904,7 +904,7 @@ fn compare_hir_and_ast_module( ) -> Option<()> { let hir_mod_name = hir_module.name(ctx.db())?; let ast_mod_name = ast_module.name()?; - if hir_mod_name.to_string() != ast_mod_name.to_string() { + if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() { return None; } diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index f75df2878671e..e4f64ccc754e0 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -158,7 +158,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.to_string() == variant_name.to_string()) + .any(|(name, _)| name.display(db).to_string() == variant_name.to_string()) } fn extract_generic_params( diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index 4c61678eab4f2..d6c59a9c82950 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -62,7 +62,9 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let assist_label = match target_name { None => format!("Change visibility to {missing_visibility}"), - Some(name) => format!("Change visibility of {name} to {missing_visibility}"), + Some(name) => { + format!("Change visibility of {} to {missing_visibility}", name.display(ctx.db())) + } }; acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { @@ -117,8 +119,11 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> let target_file = in_file_source.file_id.original_file(ctx.db()); let target_name = record_field_def.name(ctx.db()); - let assist_label = - format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}"); + let assist_label = format!( + "Change visibility of {}.{} to {missing_visibility}", + parent_name.display(ctx.db()), + target_name.display(ctx.db()) + ); acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { builder.edit_file(target_file); diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index a0813c91154f6..c6d0ebd7022f0 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -85,13 +85,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' for method in methods { let adt = ast::Adt::Struct(strukt.clone()); - let name = method.name(ctx.db()).to_string(); + let name = method.name(ctx.db()).display(ctx.db()).to_string(); // if `find_struct_impl` returns None, that means that a function named `name` already exists. let Some(impl_def) = find_struct_impl(ctx, &adt, &[name]) else { continue; }; acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), - format!("Generate delegate for `{field_name}.{}()`", method.name(ctx.db())), + format!( + "Generate delegate for `{field_name}.{}()`", + method.name(ctx.db()).display(ctx.db()) + ), target, |builder| { // Create the function @@ -101,7 +104,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' }; let method_name = method.name(ctx.db()); let vis = method_source.visibility(); - let name = make::name(&method.name(ctx.db()).to_string()); + let name = make::name(&method.name(ctx.db()).display(ctx.db()).to_string()); let params = method_source.param_list().unwrap_or_else(|| make::param_list(None, [])); let type_params = method_source.generic_param_list(); @@ -111,7 +114,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' }; let tail_expr = make::expr_method_call( make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list - make::name_ref(&method_name.to_string()), + make::name_ref(&method_name.display(ctx.db()).to_string()), arg_list, ); let ret_type = method_source.ret_type(); diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs index b6958e29193ca..81545396175ba 100644 --- a/crates/ide-assists/src/handlers/generate_deref.rs +++ b/crates/ide-assists/src/handlers/generate_deref.rs @@ -70,6 +70,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( target, |edit| { generate_edit( + ctx.db(), edit, strukt, field_type.syntax(), @@ -109,6 +110,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() target, |edit| { generate_edit( + ctx.db(), edit, strukt, field_type.syntax(), @@ -121,6 +123,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() } fn generate_edit( + db: &RootDatabase, edit: &mut SourceChangeBuilder, strukt: ast::Struct, field_type_syntax: &SyntaxNode, @@ -144,7 +147,8 @@ fn generate_edit( ), }; let strukt_adt = ast::Adt::Struct(strukt); - let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code); + let deref_impl = + generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code); edit.insert(start_offset, deref_impl); } diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 739b466b311a4..4634cfc189c5b 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -196,7 +196,7 @@ fn add_func_to_accumulator( let mut func = function_template.to_string(ctx.config.snippet_cap); if let Some(name) = adt_name { // FIXME: adt may have generic params. - func = format!("\n{indent}impl {name} {{\n{func}\n{indent}}}"); + func = format!("\n{indent}impl {} {{\n{func}\n{indent}}}", name.display(ctx.db())); } builder.edit_file(file); match ctx.config.snippet_cap { diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs index d848fce4be821..b6027eac55dee 100644 --- a/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -98,7 +98,7 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> }; builder.delete(range_to_delete); - let const_ref = format!("Self::{name}"); + let const_ref = format!("Self::{}", name.display(ctx.db())); for range in usages.all().file_ranges().map(|it| it.range) { builder.replace(range, const_ref.clone()); } diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 1728c03cd03e2..917d0b3671e8e 100644 --- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.to_string(); + let module_name = module.name(ctx.db())?.display(ctx.db()).to_string(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index a7c605325ea69..166b25c69e188 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut buf = String::from("./"); match parent_module.name(ctx.db()) { Some(name) if !parent_module.is_mod_rs(ctx.db()) => { - format_to!(buf, "{name}/") + format_to!(buf, "{}/", name.display(ctx.db())) } _ => (), } diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 076d25411a818..b73270cd05fb5 100644 --- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.to_string(); + let module_name = module.name(ctx.db())?.display(ctx.db()).to_string(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index e759e1561cbd0..239149dc411d6 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -86,7 +86,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option acc.add_group( &group_label, AssistId("qualify_path", AssistKind::QuickFix), - label(candidate, &import), + label(ctx.db(), candidate, &import), range, |builder| { qualify_candidate.qualify( @@ -186,7 +186,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.to_string() == trait_method_name.to_string()) + .map(|name| name.display(db).to_string() == trait_method_name.to_string()) .unwrap_or(false) }) { @@ -216,14 +216,14 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { GroupLabel(format!("Qualify {name}")) } -fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { +fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { let import_path = &import.import_path; match candidate { ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => { - format!("Qualify as `{import_path}`") + format!("Qualify as `{}`", import_path.display(db)) } - _ => format!("Qualify with `{import_path}`"), + _ => format!("Qualify with `{}`", import_path.display(db)), } } diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs index cd977a68a67d7..0256256697740 100644 --- a/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/crates/ide-assists/src/handlers/reorder_fields.rs @@ -97,7 +97,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).display(ctx.db()).to_string(), idx)) .collect(); Some(res) diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs index af96950761ea0..66669662316fe 100644 --- a/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -114,7 +114,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.to_string(), idx)) + .map(|(idx, name)| (name.display(ctx.db()).to_string(), idx)) .collect(), ) } diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index c521a10fccfc3..f74ebfae02c77 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -234,7 +234,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option Option { let name = if let Some(adt) = ty.as_adt() { - let name = adt.name(db).to_string(); + let name = adt.name(db).display(db).to_string(); if WRAPPER_TYPES.contains(&name.as_str()) { let inner_ty = ty.type_arguments().next()?; @@ -258,7 +258,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option { } fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option { - let name = trait_.name(db).to_string(); + let name = trait_.name(db).display(db).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { return None; } diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 87346f501d2bb..d53e9e2fa9078 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -24,7 +24,7 @@ pub(crate) mod env_vars; use std::iter; use hir::{known, HasAttrs, ScopeDef, Variant}; -use ide_db::{imports::import_assets::LocatedImport, SymbolKind}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::ast; use crate::{ @@ -62,8 +62,8 @@ impl From for Vec { impl Builder { /// Convenience method, which allows to add a freshly created completion into accumulator /// without binding it to the variable. - pub(crate) fn add_to(self, acc: &mut Completions) { - acc.add(self.build()) + pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) { + acc.add(self.build(db)) } } @@ -80,7 +80,7 @@ impl Completions { pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) { let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword); - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) { @@ -134,7 +134,7 @@ impl Completions { item.insert_text(if snippet.contains('$') { kw } else { snippet }); } }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_keyword_snippet( @@ -149,7 +149,7 @@ impl Completions { Some(cap) => item.insert_snippet(cap, snippet), None => item.insert_text(if snippet.contains('$') { kw } else { snippet }), }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_crate_roots( @@ -190,7 +190,7 @@ impl Completions { local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -216,7 +216,7 @@ impl Completions { local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -276,7 +276,7 @@ impl Completions { local_name, mac, ) - .build(), + .build(ctx.db), ); } @@ -305,7 +305,7 @@ impl Completions { local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -336,7 +336,7 @@ impl Completions { local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -367,7 +367,7 @@ impl Completions { None, func, ) - .build(), + .build(ctx.db), ); } @@ -429,7 +429,7 @@ impl Completions { if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path)) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -452,7 +452,7 @@ impl Completions { if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -497,7 +497,7 @@ impl Completions { if let Some(builder) = render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -530,11 +530,12 @@ impl Completions { pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str()) - .add_to(self) + .add_to(self, ctx.db) } pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { - CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self) + CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()) + .add_to(self, ctx.db) } pub(crate) fn add_variant_pat( diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 13c5832a519b1..466f0b1fb7f9b 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path( } if is_inner || !attr_completion.prefer_inner { - item.add_to(acc); + item.add_to(acc, ctx.db); } }; diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs index 7ef4ff30b56c7..19bfd294b25cc 100644 --- a/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/crates/ide-completion/src/completions/attribute/cfg.rs @@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item); completion.insert_text(format!(r#""{item}""#)); - acc.add(completion.build()); + acc.add(completion.build(ctx.db)); }; let previous = iter::successors(ctx.original_token.prev_token(), |t| { @@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); item.insert_text(insert_text); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), }; } diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index a92fe6c7bc1a2..9447bc7db0a79 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path( item.documentation(docs); } item.lookup_by(lookup); - item.add_to(acc); + item.add_to(acc, ctx.db); } None => acc.add_macro(ctx, path_ctx, mac, name), } diff --git a/crates/ide-completion/src/completions/attribute/lint.rs b/crates/ide-completion/src/completions/attribute/lint.rs index 818c3cfd5fe7f..6bc6f34ed41b3 100644 --- a/crates/ide-completion/src/completions/attribute/lint.rs +++ b/crates/ide-completion/src/completions/attribute/lint.rs @@ -56,6 +56,6 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label); item.documentation(hir::Documentation::new(description.to_owned())); - item.add_to(acc) + item.add_to(acc, ctx.db) } } diff --git a/crates/ide-completion/src/completions/attribute/repr.rs b/crates/ide-completion/src/completions/attribute/repr.rs index a29417133e68b..14f464b7753d7 100644 --- a/crates/ide-completion/src/completions/attribute/repr.rs +++ b/crates/ide-completion/src/completions/attribute/repr.rs @@ -37,7 +37,7 @@ pub(super) fn complete_repr( if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { item.insert_snippet(cap, snippet); } - item.add_to(acc); + item.add_to(acc, ctx.db); } } } diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index ba7663401613a..57a784c45b614 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -23,7 +23,7 @@ pub(crate) fn complete_dot( let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); item.detail("expr.await"); - item.add_to(acc); + item.add_to(acc, ctx.db); } if let DotAccessKind::Method { .. } = dot_access.kind { diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index c525e0c80764c..419b864565559 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -40,7 +40,7 @@ pub(crate) fn complete_cargo_env_vars( CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| { let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); item.detail(detail); - item.add_to(acc); + item.add_to(acc, ctx.db); }); Some(()) diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs index 4e89ef6960821..c717a9cb55b8e 100644 --- a/crates/ide-completion/src/completions/extern_abi.rs +++ b/crates/ide-completion/src/completions/extern_abi.rs @@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ pub(crate) fn complete_extern_abi( acc: &mut Completions, - _ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { @@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi( let abi_str = expanded; let source_range = abi_str.text_range_between_quotes()?; for &abi in SUPPORTED_CALLING_CONVENTIONS { - CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc); + CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db); } Some(()) } diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 1edf636aea0f0..39c1b7f7b3fb4 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -273,7 +273,7 @@ fn import_on_the_fly( .filter_map(|import| { render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) }) - .map(|builder| builder.build()) + .map(|builder| builder.build(ctx.db)) .for_each(|item| acc.add(item)); Some(()) } @@ -315,7 +315,7 @@ fn import_on_the_fly_pat_( .filter_map(|import| { render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import) }) - .map(|builder| builder.build()) + .map(|builder| builder.build(ctx.db)) .for_each(|item| acc.add(item)); Some(()) } diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index 734e1bed8df1c..8b38d4f01f673 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs @@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param( }; // Completion lookup is omitted intentionally here. // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073 - item.add_to(acc) + item.add_to(acc, ctx.db) }; match kind { @@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param( ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format!("{name}: {ty}")); + add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db))); }); } } @@ -100,7 +100,9 @@ fn fill_fn_params( if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string()); + file_params + .entry(format!("{}: {ty}", name.display(ctx.db))) + .or_insert(name.display(ctx.db).to_string()); }); } remove_duplicated(&mut file_params, param_list.params()); diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs index 5c46c5806e65e..8e904fd605a8d 100644 --- a/crates/ide-completion/src/completions/format_string.rs +++ b/crates/ide-completion/src/completions/format_string.rs @@ -32,7 +32,7 @@ pub(crate) fn format_string( let source_range = TextRange::new(brace_offset, cursor); ctx.locals.iter().for_each(|(name, _)| { CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str()) - .add_to(acc); + .add_to(acc, ctx.db); }) } diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index e82908a361e60..7de1bf2dc131f 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -182,7 +182,7 @@ fn add_function_impl( let label = format!( "fn {}({})", - fn_name, + fn_name.display(ctx.db), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -193,7 +193,7 @@ fn add_function_impl( }; let mut item = CompletionItem::new(completion_kind, replacement_range, label); - item.lookup_by(format!("fn {fn_name}")) + item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) .set_documentation(func.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); @@ -216,7 +216,7 @@ fn add_function_impl( item.text_edit(TextEdit::replace(replacement_range, header)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -300,7 +300,7 @@ fn add_type_alias_impl( item.text_edit(TextEdit::replace(replacement_range, decl)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -340,7 +340,7 @@ fn add_const_impl( ), None => item.text_edit(TextEdit::replace(replacement_range, replacement)), }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index 950731eb4ca8c..d3e75c6da4761 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -52,7 +52,7 @@ pub(crate) fn complete_mod( let existing_mod_declarations = current_module .children(ctx.db) - .filter_map(|module| Some(module.name(ctx.db)?.to_string())) + .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string())) .filter(|module| module != ctx.original_token.text()) .collect::>(); @@ -99,7 +99,7 @@ pub(crate) fn complete_mod( label.push(';'); } let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label); - item.add_to(acc) + item.add_to(acc, ctx.db) }); Some(()) diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index c55bd9aaae521..2ffe123374471 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -64,7 +64,7 @@ pub(crate) fn complete_postfix( &format!("drop($0{receiver_text})"), ); item.set_documentation(drop_fn.docs(ctx.db)); - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -78,14 +78,14 @@ pub(crate) fn complete_postfix( "if let Ok {}", &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -93,22 +93,22 @@ pub(crate) fn complete_postfix( "if let Some {}", &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Some {}", &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}")) - .add_to(acc); - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc); + .add_to(acc, ctx.db); + postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { if receiver_ty.impls_trait(ctx.db, trait_, &[]) { postfix_snippet( @@ -116,12 +116,12 @@ pub(crate) fn complete_postfix( "for ele in expr {}", &format!("for ele in {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc); - postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc); + postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); let mut unsafe_should_be_wrapped = true; if dot_receiver.syntax().kind() == BLOCK_EXPR { @@ -137,7 +137,7 @@ pub(crate) fn complete_postfix( } else { format!("unsafe {receiver_text}") }; - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc); + postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -162,7 +162,7 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -172,7 +172,7 @@ pub(crate) fn complete_postfix( "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}" ), ) - .add_to(acc); + .add_to(acc, ctx.db); } }, None => { @@ -181,20 +181,23 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc); + postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + .add_to(acc, ctx.db); + postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + .add_to(acc, ctx.db); if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) { if matches!(parent.kind(), STMT_LIST | EXPR_STMT) { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc); + postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) - .add_to(acc); + .add_to(acc, ctx.db); } } @@ -315,7 +318,7 @@ fn add_custom_postfix_completions( for import in imports.into_iter() { builder.add_import(import); } - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index dfcc78e92308d..cb242e4aa6869 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions( format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", ")) }; - postfix_snippet(label, macro_name, &snippet).add_to(acc); + postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); } } } diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 0521e735dedf1..945c3945bfa39 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields( let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); - item.add_to(acc); + item.add_to(acc, ctx.db); return; } missing_fields @@ -98,7 +98,7 @@ pub(crate) fn add_default_update( postfix_match: Some(CompletionRelevancePostfixMatch::Exact), ..Default::default() }); - item.add_to(acc); + item.add_to(acc, ctx.db); } } diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs index da1f0542d286f..e9831a5b2a121 100644 --- a/crates/ide-completion/src/completions/snippet.rs +++ b/crates/ide-completion/src/completions/snippet.rs @@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet( } if in_block_expr { - snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); - snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); + snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db); + snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db); let item = snippet( ctx, cap, @@ -45,7 +45,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -88,7 +88,7 @@ mod tests { }", ); item.lookup_by("tmod"); - item.add_to(acc); + item.add_to(acc, ctx.db); let mut item = snippet( ctx, @@ -101,7 +101,7 @@ fn ${1:feature}() { }", ); item.lookup_by("tfn"); - item.add_to(acc); + item.add_to(acc, ctx.db); let item = snippet( ctx, @@ -114,7 +114,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -146,7 +146,7 @@ fn add_custom_completions( builder.add_import(import); } builder.set_detail(snip.description.clone()); - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index 45be4fb20540c..7a60030e9ef72 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -75,7 +75,7 @@ pub(crate) fn complete_use_path( is_name_already_imported, ..Default::default() }); - acc.add(builder.build()); + acc.add(builder.build(ctx.db)); } } } @@ -108,9 +108,9 @@ pub(crate) fn complete_use_path( let item = CompletionItem::new( CompletionItemKind::SymbolKind(SymbolKind::Enum), ctx.source_range(), - format!("{}::", e.name(ctx.db)), + format!("{}::", e.name(ctx.db).display(ctx.db)), ); - acc.add(item.build()); + acc.add(item.build(ctx.db)); } } _ => {} diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 94787abf73b06..cc5221cfccbdc 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1190,7 +1190,7 @@ fn pattern_context_for( }) }).and_then(|variants| { Some(variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).to_string(); + let variant_name = variant.name(sema.db).display(sema.db).to_string(); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index e95cae9d67ff0..e850f7bfdf3f9 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -3,7 +3,7 @@ use std::fmt; use hir::{Documentation, Mutability}; -use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind}; use itertools::Itertools; use smallvec::SmallVec; use stdx::{impl_from, never}; @@ -76,7 +76,8 @@ pub struct CompletionItem { pub ref_match: Option<(Mutability, TextSize)>, /// The import data to add to completion's edits. - pub import_to_add: SmallVec<[LocatedImport; 1]>, + /// (ImportPath, LastSegment) + pub import_to_add: SmallVec<[(String, String); 1]>, } // We use custom debug for CompletionItem to make snapshot tests more readable. @@ -418,7 +419,7 @@ impl Builder { ) } - pub(crate) fn build(self) -> CompletionItem { + pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem { let _p = profile::span("item::Builder::build"); let mut label = self.label; @@ -433,7 +434,7 @@ impl Builder { if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one if let Some(original_path) = import_edit.original_path.as_ref() { - label = SmolStr::from(format!("{label} (use {original_path})")); + label = SmolStr::from(format!("{label} (use {})", original_path.display(db))); } } else if let Some(trait_name) = self.trait_name { label = SmolStr::from(format!("{label} (as {trait_name})")); @@ -444,6 +445,17 @@ impl Builder { None => TextEdit::replace(self.source_range, insert_text), }; + let import_to_add = self + .imports_to_add + .into_iter() + .filter_map(|import| { + Some(( + import.import_path.display(db).to_string(), + import.import_path.segments().last()?.display(db).to_string(), + )) + }) + .collect(); + CompletionItem { source_range: self.source_range, label, @@ -457,7 +469,7 @@ impl Builder { trigger_call_info: self.trigger_call_info, relevance: self.relevance, ref_match: self.ref_match, - import_to_add: self.imports_to_add, + import_to_add, } } pub(crate) fn lookup_by(&mut self, lookup: impl Into) -> &mut Builder { diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index a88798563de48..106d4e1e52f9a 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -243,7 +243,7 @@ pub fn resolve_completion_edits( config.prefer_no_std, ) }) - .find(|mod_path| mod_path.to_string() == full_import_path); + .find(|mod_path| mod_path.display(db).to_string() == full_import_path); if let Some(import_path) = import { insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use); } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 9673252ff5219..1953eb4795772 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -126,24 +126,25 @@ pub(crate) fn render_field( field: hir::Field, ty: &hir::Type, ) -> CompletionItem { + let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); - let name = field.name(ctx.db()); + let name = field.name(db); let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &name), + field_with_receiver(db, receiver.as_ref(), &name), ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), ..CompletionRelevance::default() }); - item.detail(ty.display(ctx.db()).to_string()) - .set_documentation(field.docs(ctx.db())) + item.detail(ty.display(db).to_string()) + .set_documentation(field.docs(db)) .set_deprecated(is_deprecated) .lookup_by(name); - item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); + item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name)); if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { @@ -152,11 +153,18 @@ pub(crate) fn render_field( } } item.doc_aliases(ctx.doc_aliases); - item.build() + item.build(db) } -fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr { - receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into()) +fn field_with_receiver( + db: &RootDatabase, + receiver: Option<&hir::Name>, + field_name: &str, +) -> SmolStr { + receiver.map_or_else( + || field_name.into(), + |receiver| format!("{}.{field_name}", receiver.display(db)).into(), + ) } pub(crate) fn render_tuple_field( @@ -168,10 +176,10 @@ pub(crate) fn render_tuple_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &field.to_string()), + field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()), ); item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); - item.build() + item.build(ctx.db()) } pub(crate) fn render_type_inference( @@ -181,7 +189,7 @@ pub(crate) fn render_type_inference( let mut builder = CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string); builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); - builder.build() + builder.build(ctx.db) } pub(crate) fn render_path_resolution( @@ -319,7 +327,7 @@ fn render_resolution_path( item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{local_name}<$0>")); + .insert_snippet(cap, format!("{}<$0>", local_name.display(db))); } } } diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index 70b19988ca733..3c73983c39a2b 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs @@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index bfcd23280ce30..8afce8db5ea86 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -52,8 +52,13 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format!("{}.{}", receiver.unescaped(), name.unescaped()).into(), - format!("{receiver}.{name}").into(), + format!( + "{}.{}", + receiver.unescaped().display(ctx.db()), + name.unescaped().display(ctx.db()) + ) + .into(), + format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(), ), _ => (name.unescaped().to_smol_str(), name.to_smol_str()), }; diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index ed78fcd8e6529..728d236dff476 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -71,8 +71,10 @@ fn render( } None => (name.clone().into(), name.into(), false), }; - let (qualified_name, escaped_qualified_name) = - (qualified_name.unescaped().to_string(), qualified_name.to_string()); + let (qualified_name, escaped_qualified_name) = ( + qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display(ctx.db()).to_string(), + ); let snippet_cap = ctx.snippet_cap(); let mut rendered = match kind { @@ -98,7 +100,7 @@ fn render( } let label = format_literal_label(&qualified_name, kind, snippet_cap); let lookup = if qualified { - format_literal_lookup(&short_qualified_name.to_string(), kind) + format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind) } else { format_literal_lookup(&qualified_name, kind) }; diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 9225c91bebf51..d06abc5e91e0b 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat( let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db()); let (name, escaped_name) = match path { - Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), + Some(path) => ( + path.unescaped().display(ctx.db()).to_string().into(), + path.display(ctx.db()).to_string().into(), + ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); (name.unescaped().to_smol_str(), name.to_smol_str()) @@ -121,7 +124,7 @@ fn build_completion( Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), None => item.insert_text(pat), }; - item.build() + item.build(ctx.db()) } fn render_pat( @@ -172,7 +175,7 @@ fn render_record_as_pat( format!( "{name} {{ {}{} }}", fields.enumerate().format_with(", ", |(idx, field), f| { - f(&format_args!("{}${}", field.name(db), idx + 1)) + f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1)) }), if fields_omitted { ", .." } else { "" }, name = name diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index fbe120d2ac949..343ba7e28d8e0 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs @@ -53,5 +53,5 @@ fn render( } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index 6e0c53ec94c43..93e943dbed98e 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -21,8 +21,10 @@ pub(crate) fn render_union_literal( let name = local_name.unwrap_or_else(|| un.name(ctx.db())); let (qualified_name, escaped_qualified_name) = match path { - Some(p) => (p.unescaped().to_string(), p.to_string()), - None => (name.unescaped().to_string(), name.to_string()), + Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()), + None => { + (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string()) + } }; let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap()); let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record); @@ -51,9 +53,9 @@ pub(crate) fn render_union_literal( format!( "{} {{ {} }}", escaped_qualified_name, - fields - .iter() - .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) }) + fields.iter().format_with(", ", |field, f| { + f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db()))) + }) ) }; @@ -61,7 +63,11 @@ pub(crate) fn render_union_literal( "{} {{ {}{} }}", qualified_name, fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db()))) + f(&format_args!( + "{}: {}", + field.name(ctx.db()).display(ctx.db()), + field.ty(ctx.db()).display(ctx.db()) + )) }), if fields_omitted { ", .." } else { "" } ); @@ -76,5 +82,5 @@ pub(crate) fn render_union_literal( None => item.insert_text(literal), }; - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index 55c55725be4f9..a9a01a3a30f5b 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -27,14 +27,14 @@ pub(crate) fn render_record_lit( } let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { - f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) + f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1)) } else { - f(&format_args!("{}: ()", field.name(db))) + f(&format_args!("{}: ()", field.name(db).display(db.upcast()))) } }); let types = fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db))) + f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db))) }); RenderedLiteral { diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index ef4c939fe6493..2464e8d5f8175 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -198,11 +198,11 @@ pub(crate) fn check_edit_with_config( &db, &config, position, - completion.import_to_add.iter().filter_map(|import_edit| { - let import_path = &import_edit.import_path; - let import_name = import_path.segments().last()?; - Some((import_path.to_string(), import_name.to_string())) - }), + completion + .import_to_add + .iter() + .cloned() + .filter_map(|(import_path, import_name)| Some((import_path, import_name))), ) .into_iter() .flatten() diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index b26b0a9087ea2..901d592c69104 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -362,12 +362,12 @@ fn import_for_item( let original_item_candidate = item_for_path_search(db, original_item)?; let import_path_candidate = mod_path(original_item_candidate)?; - let import_path_string = import_path_candidate.to_string(); + let import_path_string = import_path_candidate.display(db).to_string(); let expected_import_end = if item_as_assoc(db, original_item).is_some() { unresolved_qualifier.to_string() } else { - format!("{unresolved_qualifier}::{}", item_name(db, original_item)?) + format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db)) }; if !import_path_string.contains(unresolved_first_segment) || !import_path_string.ends_with(&expected_import_end) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 9aadf40363853..5b2558ea8fe03 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -202,12 +202,13 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => { - Some((format!("../{}", mod_name.unescaped()), format!("../{new_name}"))) - } + (true, _, Some(mod_name)) => Some(( + format!("../{}", mod_name.unescaped().display(sema.db)), + format!("../{new_name}"), + )), // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().to_string(), new_name.to_owned())) + Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) } _ => None, }; diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs index 6a7ea7c19f228..9abbc34414291 100644 --- a/crates/ide-db/src/traits.rs +++ b/crates/ide-db/src/traits.rs @@ -38,15 +38,15 @@ pub fn get_missing_assoc_items( for item in imp.items(sema.db) { match item { hir::AssocItem::Function(it) => { - impl_fns_consts.insert(it.name(sema.db).to_string()); + impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string()); } hir::AssocItem::Const(it) => { if let Some(name) = it.name(sema.db) { - impl_fns_consts.insert(name.to_string()); + impl_fns_consts.insert(name.display(sema.db).to_string()); } } hir::AssocItem::TypeAlias(it) => { - impl_type.insert(it.name(sema.db).to_string()); + impl_type.insert(it.name(sema.db).display(sema.db).to_string()); } } } @@ -57,12 +57,14 @@ pub fn get_missing_assoc_items( .into_iter() .filter(|i| match i { hir::AssocItem::Function(f) => { - !impl_fns_consts.contains(&f.name(sema.db).to_string()) + !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string()) + } + hir::AssocItem::TypeAlias(t) => { + !impl_type.contains(&t.name(sema.db).display(sema.db).to_string()) } - hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), hir::AssocItem::Const(c) => c .name(sema.db) - .map(|n| !impl_fns_consts.contains(&n.to_string())) + .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string())) .unwrap_or_default(), }) .collect() @@ -137,7 +139,7 @@ mod tests { sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block); let actual = match trait_ { - Some(trait_) => trait_.name(&db).to_string(), + Some(trait_) => trait_.name(&db).display(&db).to_string(), None => String::new(), }; expect.assert_eq(&actual); @@ -152,7 +154,7 @@ mod tests { let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); let actual = items .into_iter() - .map(|item| item.name(&db).unwrap().to_string()) + .map(|item| item.name(&db).unwrap().display(&db).to_string()) .collect::>() .join("\n"); expect.assert_eq(&actual); diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 17a2b44ccd5f3..60ccc41df01cc 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -31,7 +31,7 @@ use crate::{fix, Diagnostic, DiagnosticsContext}; pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { let mut message = String::from("missing structure fields:\n"); for field in &d.missed_fields { - format_to!(message, "- {}\n", field); + format_to!(message, "- {}\n", field.display(ctx.sema.db)); } let ptr = InFile::new( @@ -175,7 +175,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option ast::Type { let ty_str = match ty.as_adt() { - Some(adt) => adt.name(db).to_string(), + Some(adt) => adt.name(db).display(db.upcast()).to_string(), None => { ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string()) } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 9d6a862cb1192..0af4100180826 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -30,7 +30,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno })(); Diagnostic::new( "need-mut", - format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)), + format!( + "cannot mutate immutable variable `{}`", + d.local.name(ctx.sema.db).display(ctx.sema.db) + ), ctx.sema.diagnostics_display_range(d.span.clone()).range, ) .with_fixes(fixes) diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 67da5c7f27d14..4cd85a479a061 100644 --- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -11,7 +11,11 @@ pub(crate) fn private_assoc_item( d: &hir::PrivateAssocItem, ) -> Diagnostic { // FIXME: add quickfix - let name = d.item.name(ctx.sema.db).map(|name| format!("`{name}` ")).unwrap_or_default(); + let name = d + .item + .name(ctx.sema.db) + .map(|name| format!("`{}` ", name.display(ctx.sema.db))) + .unwrap_or_default(); Diagnostic::new( "private-assoc-item", format!( diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs index be83ad6aaadaa..de7f51f693c42 100644 --- a/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/crates/ide-diagnostics/src/handlers/private_field.rs @@ -9,8 +9,8 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) "private-field", format!( "field `{}` of `{}` is private", - d.field.name(ctx.sema.db), - d.field.parent_def(ctx.sema.db).name(ctx.sema.db) + d.field.name(ctx.sema.db).display(ctx.sema.db), + d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, ) diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index dbedf1e6c1ed9..768efecb086e5 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -8,7 +8,7 @@ pub(crate) fn undeclared_label( let name = &d.name; Diagnostic::new( "undeclared-label", - format!("use of undeclared label `{name}`"), + format!("use of undeclared label `{}`", name.display(ctx.sema.db)), ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, ) } diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 5933a9b694a37..9fedadeae0359 100644 --- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -8,7 +8,7 @@ pub(crate) fn unreachable_label( let name = &d.name; Diagnostic::new( "unreachable-label", - format!("use of unreachable label `{name}`"), + format!("use of unreachable label `{}`", name.display(ctx.sema.db)), ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, ) } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index fe2d03bb10caf..5e4efa41fd9c3 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -26,7 +26,7 @@ pub(crate) fn unresolved_field( "unresolved-field", format!( "no field `{}` on type `{}`{method_suffix}", - d.name, + d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 1a5efff2c0c60..3943b51ab422d 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -13,7 +13,7 @@ pub(crate) fn unresolved_macro_call( let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( "unresolved-macro-call", - format!("unresolved macro `{}{bang}`", d.path), + format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)), display_range, ) .experimental() diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 8a0b457857942..8bbb837e6709e 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -26,7 +26,7 @@ pub(crate) fn unresolved_method( "unresolved-method", format!( "no method `{}` on type `{}`{field_suffix}", - d.name, + d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs index f51a9547a5084..66832a0bee496 100644 --- a/crates/ide-ssr/src/lib.rs +++ b/crates/ide-ssr/src/lib.rs @@ -184,6 +184,7 @@ impl<'db> MatchFinder<'db> { ( file_id, replacing::matches_to_edit( + self.sema.db, &matches, &self.sema.db.file_text(file_id), &self.rules, diff --git a/crates/ide-ssr/src/replacing.rs b/crates/ide-ssr/src/replacing.rs index e27ef6e35ef42..b4b83f62da240 100644 --- a/crates/ide-ssr/src/replacing.rs +++ b/crates/ide-ssr/src/replacing.rs @@ -14,14 +14,16 @@ use crate::{fragments, resolving::ResolvedRule, Match, SsrMatches}; /// template. Placeholders in the template will have been substituted with whatever they matched to /// in the original code. pub(crate) fn matches_to_edit( + db: &dyn hir::db::ExpandDatabase, matches: &SsrMatches, file_src: &str, rules: &[ResolvedRule], ) -> TextEdit { - matches_to_edit_at_offset(matches, file_src, 0.into(), rules) + matches_to_edit_at_offset(db, matches, file_src, 0.into(), rules) } fn matches_to_edit_at_offset( + db: &dyn hir::db::ExpandDatabase, matches: &SsrMatches, file_src: &str, relative_start: TextSize, @@ -31,13 +33,14 @@ fn matches_to_edit_at_offset( for m in &matches.matches { edit_builder.replace( m.range.range.checked_sub(relative_start).unwrap(), - render_replace(m, file_src, rules), + render_replace(db, m, file_src, rules), ); } edit_builder.finish() } struct ReplacementRenderer<'a> { + db: &'a dyn hir::db::ExpandDatabase, match_info: &'a Match, file_src: &'a str, rules: &'a [ResolvedRule], @@ -53,13 +56,19 @@ struct ReplacementRenderer<'a> { placeholder_tokens_requiring_parenthesis: FxHashSet, } -fn render_replace(match_info: &Match, file_src: &str, rules: &[ResolvedRule]) -> String { +fn render_replace( + db: &dyn hir::db::ExpandDatabase, + match_info: &Match, + file_src: &str, + rules: &[ResolvedRule], +) -> String { let rule = &rules[match_info.rule_index]; let template = rule .template .as_ref() .expect("You called MatchFinder::edits after calling MatchFinder::add_search_pattern"); let mut renderer = ReplacementRenderer { + db, match_info, file_src, rules, @@ -96,7 +105,7 @@ impl ReplacementRenderer<'_> { fn render_node(&mut self, node: &SyntaxNode) { if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) { - self.out.push_str(&mod_path.to_string()); + self.out.push_str(&mod_path.display(self.db).to_string()); // Emit everything except for the segment's name-ref, since we already effectively // emitted that as part of `mod_path`. if let Some(path) = ast::Path::cast(node.clone()) { @@ -144,6 +153,7 @@ impl ReplacementRenderer<'_> { ); } let edit = matches_to_edit_at_offset( + self.db, &placeholder_value.inner_matches, self.file_src, range.start(), diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 597b28d36d7f7..8112c4f7259cf 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -394,7 +394,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name)); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db))); path }) } @@ -567,9 +567,9 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { - Adt::Struct(s) => format!("struct.{}.html", s.name(db)), - Adt::Enum(e) => format!("enum.{}.html", e.name(db)), - Adt::Union(u) => format!("union.{}.html", u.name(db)), + Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())), + Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())), }, Definition::Module(m) => match m.name(db) { // `#[doc(keyword = "...")]` is internal used only by rust compiler @@ -577,21 +577,25 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw.trim_matches('"')) } - None => format!("{name}/index.html"), + None => format!("{}/index.html", name.display(db.upcast())), }, 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)), + Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())), + Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())), + Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())), + Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())), + Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())), Definition::Variant(ev) => { - format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) + format!( + "enum.{}.html#variant.{}", + ev.parent_enum(db).name(db).display(db.upcast()), + ev.name(db).display(db.upcast()) + ) } - Definition::Const(c) => format!("const.{}.html", c.name(db)?), - Definition::Static(s) => format!("static.{}.html", s.name(db)), - Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)), + Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())), + Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())), + Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())), Definition::Field(field) => { let def = match field.parent_def(db) { hir::VariantDef::Struct(it) => Definition::Adt(it.into()), @@ -599,7 +603,11 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some((def, file, Some(format!("structfield.{}", field.name(db))))); + return Some(( + def, + file, + Some(format!("structfield.{}", field.name(db).display(db.upcast()))), + )); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -633,12 +641,14 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db)) + format!("tymethod.{}", function.name(db).display(db.upcast())) } else { - format!("method.{}", function.name(db)) + format!("method.{}", function.name(db).display(db.upcast())) } } - AssocItem::Const(constant) => format!("associatedconstant.{}", constant.name(db)?), - AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db)), + AssocItem::Const(constant) => { + format!("associatedconstant.{}", constant.name(db)?.display(db.upcast())) + } + AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())), }) } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 8c03d197e1c77..d6bbf2bf794dc 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -76,7 +76,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(item) = ast::Item::cast(node.clone()) { if let Some(def) = sema.resolve_attr_macro_call(&item) { break ( - def.name(db).to_string(), + def.name(db).display(db).to_string(), expand_attr_macro_recur(&sema, &item)?, SyntaxKind::MACRO_ITEMS, ); diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index b6279295ce3fd..1a84a963f55b6 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -56,7 +56,7 @@ impl HoverAction { mod_path: render::path( db, it.module(db)?, - it.name(db).map(|name| name.to_string()), + it.name(db).map(|name| name.display(db).to_string()), ), nav: it.try_to_nav(db)?, }) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index fffc837876af8..d9d4a1a99212d 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -370,7 +370,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option Definition::Variant(e) => Some(e.parent_enum(db).name(db)), _ => None, } - .map(|name| name.to_string()) + .map(|name| name.display(db).to_string()) } pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option) -> String { @@ -380,7 +380,7 @@ pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option { return famous_defs .and_then(|fd| builtin(fd, it)) - .or_else(|| Some(Markup::fenced_block(&it.name()))) + .or_else(|| Some(Markup::fenced_block(&it.name().display(db)))) } Definition::Local(it) => return local(db, it, config), Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } Definition::GenericParam(it) => label_and_docs(db, it), - Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))), + Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), // FIXME: We should be able to show more info about these Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), - Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None), + Definition::DeriveHelper(it) => { + (format!("derive_helper {}", it.name(db).display(db)), None) + } }; let docs = docs @@ -717,19 +719,19 @@ fn markup(docs: Option, desc: String, mod_path: Option) -> Optio fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option { // std exposes prim_{} modules with docstrings on the root to document the builtins - let primitive_mod = format!("prim_{}", builtin.name()); + let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db)); let doc_owner = find_std_module(famous_defs, &primitive_mod)?; let docs = doc_owner.attrs(famous_defs.0.db).docs()?; - markup(Some(docs.into()), builtin.name().to_string(), None) + markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None) } fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option { let db = famous_defs.0.db; let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(db); - std_root_module - .children(db) - .find(|module| module.name(db).map_or(false, |module| module.to_string() == name)) + std_root_module.children(db).find(|module| { + module.name(db).map_or(false, |module| module.display(db).to_string() == name) + }) } fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option { @@ -748,7 +750,7 @@ fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option format!("{is_mut}self: {ty}"), }; diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index cd5a815abb0c4..2cefd5acdc2e4 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -35,7 +35,7 @@ pub(super) fn hints( let ty = imp.self_ty(sema.db); let trait_ = imp.trait_(sema.db); let hint_text = match trait_ { - Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)), + Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)), None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)), }; (hint_text, None) diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 7f36e1df547a3..0d57e63d29c91 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -1,7 +1,7 @@ //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) //! for LSIF and LSP. -use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; +use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics}; use ide_db::{ base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, defs::{Definition, IdentClass}, @@ -27,7 +27,7 @@ pub enum MonikerDescriptorKind { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MonikerDescriptor { - pub name: Name, + pub name: String, pub desc: MonikerDescriptorKind, } @@ -41,11 +41,7 @@ impl ToString for MonikerIdentifier { fn to_string(&self) -> String { match self { MonikerIdentifier { description, crate_name } => { - format!( - "{}::{}", - crate_name, - description.iter().map(|x| x.name.to_string()).join("::") - ) + format!("{}::{}", crate_name, description.iter().map(|x| &x.name).join("::")) } } } @@ -136,7 +132,10 @@ pub(crate) fn def_to_moniker( let krate = module.krate(); let mut description = vec![]; description.extend(module.path_to_root(db).into_iter().filter_map(|x| { - Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace }) + Some(MonikerDescriptor { + name: x.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Namespace, + }) })); // Handle associated items within a trait @@ -147,7 +146,7 @@ pub(crate) fn def_to_moniker( // Because different traits can have functions with the same name, // we have to include the trait name as part of the moniker for uniqueness. description.push(MonikerDescriptor { - name: trait_.name(db), + name: trait_.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -156,14 +155,14 @@ pub(crate) fn def_to_moniker( // we add both the struct name and the trait name to the path if let Some(adt) = impl_.self_ty(db).as_adt() { description.push(MonikerDescriptor { - name: adt.name(db), + name: adt.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } if let Some(trait_) = impl_.trait_(db) { description.push(MonikerDescriptor { - name: trait_.name(db), + name: trait_.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -173,7 +172,7 @@ pub(crate) fn def_to_moniker( if let Definition::Field(it) = def { description.push(MonikerDescriptor { - name: it.parent_def(db).name(db), + name: it.parent_def(db).name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -191,48 +190,63 @@ pub(crate) fn def_to_moniker( return None; } - MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter } - } - Definition::Macro(m) => { - MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro } - } - Definition::Function(f) => { - MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method } - } - Definition::Variant(v) => { - MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::Const(c) => { - MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term } - } - 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 } - } - Definition::Module(m) => { - MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace } - } - Definition::BuiltinType(b) => { - MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type } + MonikerDescriptor { + name: local.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Parameter, + } } + Definition::Macro(m) => MonikerDescriptor { + name: m.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Macro, + }, + Definition::Function(f) => MonikerDescriptor { + name: f.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Method, + }, + Definition::Variant(v) => MonikerDescriptor { + name: v.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::Const(c) => MonikerDescriptor { + name: c.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Term, + }, + Definition::Trait(trait_) => MonikerDescriptor { + name: trait_.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::TraitAlias(ta) => MonikerDescriptor { + name: ta.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::TypeAlias(ta) => MonikerDescriptor { + name: ta.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::TypeParameter, + }, + Definition::Module(m) => MonikerDescriptor { + name: m.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Namespace, + }, + Definition::BuiltinType(b) => MonikerDescriptor { + name: b.name().display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, Definition::SelfType(imp) => MonikerDescriptor { - name: imp.self_ty(db).as_adt()?.name(db), + name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }, - Definition::Field(it) => { - MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term } - } - Definition::Adt(adt) => { - MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::Static(s) => { - MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta } - } + Definition::Field(it) => MonikerDescriptor { + name: it.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Term, + }, + Definition::Adt(adt) => MonikerDescriptor { + name: adt.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::Static(s) => MonikerDescriptor { + name: s.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Meta, + }, }; description.push(name_desc); diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 64150cc2f7fdb..78972241bdfc1 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -349,8 +349,13 @@ pub(crate) fn runnable_mod( if !has_test_function_or_multiple_test_submodules(sema, &def) { return None; } - let path = - def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); + let path = def + .path_to_root(sema.db) + .into_iter() + .rev() + .filter_map(|it| it.name(sema.db)) + .map(|it| it.display(sema.db).to_string()) + .join("::"); let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); @@ -376,7 +381,7 @@ pub(crate) fn runnable_impl( } else { String::new() }; - let mut test_id = format!("{adt_name}{params}"); + let mut test_id = format!("{}{params}", adt_name.display(sema.db)); test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); @@ -391,8 +396,13 @@ fn runnable_mod_outline_definition( if !has_test_function_or_multiple_test_submodules(sema, &def) { return None; } - let path = - def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); + let path = def + .path_to_root(sema.db) + .into_iter() + .rev() + .filter_map(|it| it.name(sema.db)) + .map(|it| it.display(sema.db).to_string()) + .join("::"); let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); @@ -430,7 +440,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut path = String::new(); def.canonical_module_path(db)? .flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}::", name)); + .for_each(|name| format_to!(path, "{}::", name.display(db))); // This probably belongs to canonical_path? if let Some(assoc_item) = def.as_assoc_item(db) { if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) { @@ -438,17 +448,17 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { if let Some(adt) = ty.as_adt() { let name = adt.name(db); let mut ty_args = ty.generic_parameters(db).peekable(); - format_to!(path, "{}", name); + format_to!(path, "{}", name.display(db)); if ty_args.peek().is_some() { format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); } - format_to!(path, "::{}", def_name); + format_to!(path, "::{}", def_name.display(db)); path.retain(|c| c != ' '); return Some(path); } } } - format_to!(path, "{}", def_name); + format_to!(path, "{}", def_name.display(db)); Some(path) })(); diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index cdee705cbfdaa..9a0529ec20fee 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -162,7 +162,7 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(|it| it.into()); - format_to!(res.signature, "fn {}", func.name(db)); + format_to!(res.signature, "fn {}", func.name(db).display(db)); fn_params = Some(match callable.receiver_param(db) { Some(_self) => func.params_without_self(db), None => func.assoc_fn_params(db), @@ -170,15 +170,15 @@ fn signature_help_for_call( } hir::CallableKind::TupleStruct(strukt) => { res.doc = strukt.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {}", strukt.name(db)); + format_to!(res.signature, "struct {}", strukt.name(db).display(db)); } hir::CallableKind::TupleEnumVariant(variant) => { res.doc = variant.docs(db).map(|it| it.into()); format_to!( res.signature, "enum {}::{}", - variant.parent_enum(db).name(db), - variant.name(db) + variant.parent_enum(db).name(db).display(db), + variant.name(db).display(db) ); } hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (), @@ -248,31 +248,31 @@ fn signature_help_for_generics( match generics_def { hir::GenericDef::Function(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "fn {}", it.name(db)); + format_to!(res.signature, "fn {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Enum(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}", it.name(db)); + format_to!(res.signature, "enum {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Struct(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {}", it.name(db)); + format_to!(res.signature, "struct {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Union(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "union {}", it.name(db)); + format_to!(res.signature, "union {}", it.name(db).display(db)); } hir::GenericDef::Trait(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "trait {}", it.name(db)); + format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TraitAlias(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "trait {}", it.name(db)); + format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "type {}", it.name(db)); + format_to!(res.signature, "type {}", it.name(db).display(db)); } hir::GenericDef::Variant(it) => { // In paths, generics of an enum can be specified *after* one of its variants. @@ -280,7 +280,7 @@ fn signature_help_for_generics( // We'll use the signature of the enum, but include the docs of the variant. res.doc = it.docs(db).map(|it| it.into()); let enum_ = it.parent_enum(db); - format_to!(res.signature, "enum {}", enum_.name(db)); + format_to!(res.signature, "enum {}", enum_.name(db).display(db)); generics_def = enum_.into(); } // These don't have generic args that can be specified @@ -412,7 +412,12 @@ fn signature_help_for_tuple_struct_pat( let en = variant.parent_enum(db); res.doc = en.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db)); + format_to!( + res.signature, + "enum {}::{} (", + en.name(db).display(db), + variant.name(db).display(db) + ); variant.fields(db) } else { let adt = match path_res { @@ -424,7 +429,7 @@ fn signature_help_for_tuple_struct_pat( match adt { hir::Adt::Struct(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {} (", it.name(db)); + format_to!(res.signature, "struct {} (", it.name(db).display(db)); it.fields(db) } _ => return None, @@ -486,7 +491,12 @@ fn signature_help_for_record_( let en = variant.parent_enum(db); res.doc = en.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db)); + format_to!( + res.signature, + "enum {}::{} {{ ", + en.name(db).display(db), + variant.name(db).display(db) + ); } else { let adt = match path_res { PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?, @@ -498,12 +508,12 @@ fn signature_help_for_record_( hir::Adt::Struct(it) => { fields = it.fields(db); res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {} {{ ", it.name(db)); + format_to!(res.signature, "struct {} {{ ", it.name(db).display(db)); } hir::Adt::Union(it) => { fields = it.fields(db); res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "union {} {{ ", it.name(db)); + format_to!(res.signature, "union {} {{ ", it.name(db).display(db)); } _ => return None, } @@ -514,7 +524,7 @@ fn signature_help_for_record_( let mut buf = String::new(); for (field, ty) in fields2 { let name = field.name(db); - format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20))); + format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20))); res.push_record_field(&buf); buf.clear(); @@ -524,7 +534,7 @@ fn signature_help_for_record_( } for (name, field) in fields { let Some(field) = field else { continue }; - format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20))); + format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20))); res.push_record_field(&buf); buf.clear(); } diff --git a/crates/ide/src/view_item_tree.rs b/crates/ide/src/view_item_tree.rs index 9c1f93356ee2d..e072df430fc0d 100644 --- a/crates/ide/src/view_item_tree.rs +++ b/crates/ide/src/view_item_tree.rs @@ -12,5 +12,5 @@ use ide_db::RootDatabase; // | VS Code | **rust-analyzer: Debug ItemTree** // |=== pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { - db.file_item_tree(file_id.into()).pretty_print() + db.file_item_tree(file_id.into()).pretty_print(db) } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 673192f57163c..2c2a9a18d2e31 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -221,6 +221,7 @@ impl flags::AnalysisStats { .rev() .filter_map(|it| it.name(db)) .chain(Some(a.name(db))) + .map(|it| it.display(db).to_string()) .join("::"); println!("Data layout for {full_name} failed due {e:?}"); } @@ -248,6 +249,7 @@ impl flags::AnalysisStats { .rev() .filter_map(|it| it.name(db)) .chain(c.name(db)) + .map(|it| it.display(db).to_string()) .join("::"); println!("Const eval for {full_name} failed due {e:?}"); } @@ -274,6 +276,7 @@ impl flags::AnalysisStats { .rev() .filter_map(|it| it.name(db)) .chain(Some(f.name(db))) + .map(|it| it.display(db).to_string()) .join("::"); println!("Mir body for {full_name} failed due {e:?}"); } @@ -332,9 +335,10 @@ impl flags::AnalysisStats { .rev() .filter_map(|it| it.name(db)) .chain(Some(f.name(db))) + .map(|it| it.display(db).to_string()) .join("::"); if let Some(only_name) = self.only.as_deref() { - if name.to_string() != only_name && full_name != only_name { + if name.display(db).to_string() != only_name && full_name != only_name { continue; } } @@ -376,7 +380,7 @@ impl flags::AnalysisStats { end.col, )); } else { - bar.println(format!("{name}: Unknown type",)); + bar.println(format!("{}: Unknown type", name.display(db))); } } true @@ -431,7 +435,7 @@ impl flags::AnalysisStats { } else { bar.println(format!( "{}: Expected {}, got {}", - name, + name.display(db), mismatch.expected.display(db), mismatch.actual.display(db) )); @@ -479,7 +483,7 @@ impl flags::AnalysisStats { end.col, )); } else { - bar.println(format!("{name}: Unknown type",)); + bar.println(format!("{}: Unknown type", name.display(db))); } } true @@ -533,7 +537,7 @@ impl flags::AnalysisStats { } else { bar.println(format!( "{}: Expected {}, got {}", - name, + name.display(db), mismatch.expected.display(db), mismatch.actual.display(db) )); diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 1c848090b6a49..61924d5843193 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -9,7 +9,6 @@ use crate::{ cli::load_cargo::ProcMacroServerChoice, line_index::{LineEndings, LineIndex, PositionEncoding}, }; -use hir::Name; use ide::{ LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId, TokenStaticData, @@ -209,13 +208,12 @@ fn new_descriptor_str( } } -fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { - let mut name = name.to_string(); +fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { if name.contains('\'') { - name = format!("`{name}`"); + new_descriptor_str(&format!("`{name}`"), suffix) + } else { + new_descriptor_str(&name, suffix) } - - new_descriptor_str(name.as_str(), suffix) } /// Loosely based on `def_to_moniker` @@ -235,7 +233,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { .iter() .map(|desc| { new_descriptor( - desc.name.clone(), + &desc.name, match desc.desc { MonikerDescriptorKind::Namespace => Namespace, MonikerDescriptorKind::Type => Type, diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 71a57772b1fb9..8a9e947ded0ad 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -306,12 +306,10 @@ fn completion_item( let imports: Vec<_> = item .import_to_add .into_iter() - .filter_map(|import_edit| { - let import_path = &import_edit.import_path; - let import_name = import_path.segments().last()?; + .filter_map(|(import_path, import_name)| { Some(lsp_ext::CompletionImport { - full_import_path: import_path.to_string(), - imported_name: import_name.to_string(), + full_import_path: import_path, + imported_name: import_name, }) }) .collect(); From 021b7398e1c37c75c250e3b81a1b46622ce683c9 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 24 May 2023 21:39:06 +0000 Subject: [PATCH 493/806] Ignore #[cfg]'d out code in needless_else --- clippy_lints/src/needless_else.rs | 42 +++++++++++++++++-------------- tests/ui/needless_else.fixed | 17 +++++++++++++ tests/ui/needless_else.rs | 17 +++++++++++++ tests/ui/needless_else.stderr | 2 +- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs index 8faf033e40689..4ff1bf7ffc0de 100644 --- a/clippy_lints/src/needless_else.rs +++ b/clippy_lints/src/needless_else.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span, span_extract_comment}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -35,23 +36,26 @@ declare_lint_pass!(NeedlessElse => [NEEDLESS_ELSE]); impl EarlyLintPass for NeedlessElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind && - let ExprKind::Block(block, _) = &else_clause.kind && - !expr.span.from_expansion() && - !else_clause.span.from_expansion() && - block.stmts.is_empty() { - let span = trim_span(cx.sess().source_map(), expr.span.trim_start(then_block.span).unwrap()); - if span_extract_comment(cx.sess().source_map(), span).is_empty() { - span_lint_and_sugg( - cx, - NEEDLESS_ELSE, - span, - "this else branch is empty", - "you can remove it", - String::new(), - Applicability::MachineApplicable, - ); - } - } + if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind + && let ExprKind::Block(block, _) = &else_clause.kind + && !expr.span.from_expansion() + && !else_clause.span.from_expansion() + && block.stmts.is_empty() + && let Some(trimmed) = expr.span.trim_start(then_block.span) + && let span = trim_span(cx.sess().source_map(), trimmed) + && let Some(else_snippet) = snippet_opt(cx, span) + // Ignore else blocks that contain comments or #[cfg]s + && !else_snippet.contains(['/', '#']) + { + span_lint_and_sugg( + cx, + NEEDLESS_ELSE, + span, + "this else branch is empty", + "you can remove it", + String::new(), + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/needless_else.fixed b/tests/ui/needless_else.fixed index 14f81728a868a..06a16162790cc 100644 --- a/tests/ui/needless_else.fixed +++ b/tests/ui/needless_else.fixed @@ -12,6 +12,10 @@ macro_rules! mac { }; } +macro_rules! empty_expansion { + () => {}; +} + fn main() { let b = std::hint::black_box(true); @@ -37,4 +41,17 @@ fn main() { // Do not lint because inside a macro mac!(b); + + if b { + println!("Foobar"); + } else { + #[cfg(foo)] + "Do not lint cfg'd out code" + } + + if b { + println!("Foobar"); + } else { + empty_expansion!(); + } } diff --git a/tests/ui/needless_else.rs b/tests/ui/needless_else.rs index fae11818141b7..728032c47a66b 100644 --- a/tests/ui/needless_else.rs +++ b/tests/ui/needless_else.rs @@ -12,6 +12,10 @@ macro_rules! mac { }; } +macro_rules! empty_expansion { + () => {}; +} + fn main() { let b = std::hint::black_box(true); @@ -38,4 +42,17 @@ fn main() { // Do not lint because inside a macro mac!(b); + + if b { + println!("Foobar"); + } else { + #[cfg(foo)] + "Do not lint cfg'd out code" + } + + if b { + println!("Foobar"); + } else { + empty_expansion!(); + } } diff --git a/tests/ui/needless_else.stderr b/tests/ui/needless_else.stderr index a7b2f1959c77b..ea69308516449 100644 --- a/tests/ui/needless_else.stderr +++ b/tests/ui/needless_else.stderr @@ -1,5 +1,5 @@ error: this else branch is empty - --> $DIR/needless_else.rs:20:7 + --> $DIR/needless_else.rs:24:7 | LL | } else { | _______^ From 1222869b3eb66f2fc67b6780feb897eddadfdec5 Mon Sep 17 00:00:00 2001 From: alibektas Date: Sun, 14 May 2023 02:38:03 +0200 Subject: [PATCH 494/806] Fix #14557. Docs aliases can now be detected and used in searching for workspace symbols --- crates/hir/src/symbols.rs | 98 +++++---- crates/ide-db/src/symbol_index.rs | 27 +++ .../ide-db/src/test_data/test_doc_alias.txt | 202 ++++++++++++++++++ .../test_symbol_index_collection.txt | 22 ++ crates/ide/src/goto_definition.rs | 1 + crates/ide/src/lib.rs | 2 +- crates/ide/src/navigation_target.rs | 13 +- crates/ide/src/runnables.rs | 16 +- crates/rust-analyzer/src/handlers/request.rs | 5 +- 9 files changed, 338 insertions(+), 48 deletions(-) create mode 100644 crates/ide-db/src/test_data/test_doc_alias.txt diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index af37206eadc27..207e8206c92d5 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -20,6 +20,7 @@ pub struct FileSymbol { pub def: ModuleDef, pub loc: DeclarationLocation, pub container_name: Option, + pub is_alias: bool, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -249,46 +250,69 @@ impl<'a> SymbolCollector<'a> { ::Data: HasSource, <::Data as HasSource>::Value: HasName, { - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - Some(FileSymbol { - name: name_node.text().into(), - def: ModuleDef::from(id.into()), - container_name: s.current_container_name.clone(), - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } + let loc = id.lookup(self.db.upcast()); + let source = loc.source(self.db.upcast()); + let Some(name_node) = source.value.name() else { return }; + let def = ModuleDef::from(id.into()); + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); + } + } - fn push_module(&mut self, module_id: ModuleId) { - self.push_file_symbol(|s| { - let def_map = module_id.def_map(s.db.upcast()); - let module_data = &def_map[module_id.local_id]; - let declaration = module_data.origin.declaration()?; - let module = declaration.to_node(s.db.upcast()); - let name_node = module.name()?; - Some(FileSymbol { - name: name_node.text().into(), - def: ModuleDef::Module(module_id.into()), - container_name: s.current_container_name.clone(), - loc: DeclarationLocation { - hir_file_id: declaration.file_id, - ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def, + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } - fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { - if let Some(file_symbol) = f(self) { - self.symbols.push(file_symbol); + fn push_module(&mut self, module_id: ModuleId) { + let def_map = module_id.def_map(self.db.upcast()); + let module_data = &def_map[module_id.local_id]; + let Some(declaration) = module_data.origin.declaration() else { return }; + let module = declaration.to_node(self.db.upcast()); + let Some(name_node) = module.name() else { return }; + let dec_loc = DeclarationLocation { + hir_file_id: declaration.file_id, + ptr: SyntaxNodePtr::new(module.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; + + let def = ModuleDef::Module(module_id.into()); + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); + } } + + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def: ModuleDef::Module(module_id.into()), + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } } diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index fa796ae13b2bb..b54c43b296b2a 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -434,4 +434,31 @@ struct StructInModB; expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_doc_alias() { + let (db, _) = RootDatabase::with_single_file( + r#" +#[doc(alias="s1")] +#[doc(alias="s2")] +#[doc(alias("mul1","mul2"))] +struct Struct; + +#[doc(alias="s1")] +struct Duplicate; + "#, + ); + + let symbols: Vec<_> = Crate::from(db.test_crate()) + .modules(&db) + .into_iter() + .map(|module_id| { + let mut symbols = SymbolCollector::collect_module(&db, module_id); + symbols.sort_by_key(|it| it.name.clone()); + (module_id, symbols) + }) + .collect(); + + expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols); + } } diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt new file mode 100644 index 0000000000000..77714efa35040 --- /dev/null +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -0,0 +1,202 @@ +[ + ( + Module { + id: ModuleId { + krate: Idx::(0), + block: None, + local_id: Idx::(0), + }, + }, + [ + FileSymbol { + name: "Duplicate", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "mul1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "mul2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + ], + ), +] diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1e34dd633c84c..b5adfc13d9644 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -31,6 +31,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST", @@ -55,6 +56,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST_WITH_INNER", @@ -79,6 +81,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Enum", @@ -105,6 +108,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Macro", @@ -131,6 +135,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "STATIC", @@ -155,6 +160,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Struct", @@ -181,6 +187,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "StructFromMacro", @@ -207,6 +214,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "StructInFn", @@ -235,6 +243,7 @@ container_name: Some( "main", ), + is_alias: false, }, FileSymbol { name: "StructInNamedConst", @@ -263,6 +272,7 @@ container_name: Some( "CONST_WITH_INNER", ), + is_alias: false, }, FileSymbol { name: "StructInUnnamedConst", @@ -289,6 +299,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Trait", @@ -313,6 +324,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Union", @@ -339,6 +351,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "a_mod", @@ -365,6 +378,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "b_mod", @@ -391,6 +405,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "define_struct", @@ -417,6 +432,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "impl_fn", @@ -441,6 +457,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "macro_rules_macro", @@ -467,6 +484,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "main", @@ -491,6 +509,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "trait_fn", @@ -517,6 +536,7 @@ container_name: Some( "Trait", ), + is_alias: false, }, ], ), @@ -554,6 +574,7 @@ }, }, container_name: None, + is_alias: false, }, ], ), @@ -591,6 +612,7 @@ }, }, container_name: None, + is_alias: false, }, ], ), diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index ef3f14d79d142..4e641357e3728 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -113,6 +113,7 @@ fn try_lookup_include_path( file_id, full_range: TextRange::new(0.into(), size), name: path.into(), + alias: None, focus_range: None, kind: None, container_name: None, diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 72d20af663756..4e5f01e716691 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -405,7 +405,7 @@ impl Analysis { self.with_db(|db| { symbol_index::world_symbols(db, query) .into_iter() // xx: should we make this a par iter? - .filter_map(|s| s.def.try_to_nav(db)) + .filter_map(|s| s.try_to_nav(db)) .collect::>() }) } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index d8ce79de3755e..65c37ca68c522 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -45,6 +45,9 @@ pub struct NavigationTarget { pub container_name: Option, pub description: Option, pub docs: Option, + /// In addition to a `name` field, a `NavigationTarget` may also be aliased + /// In such cases we want a `NavigationTarget` to be accessible by its alias + pub alias: Option, } impl fmt::Debug for NavigationTarget { @@ -154,6 +157,7 @@ impl NavigationTarget { container_name: None, description: None, docs: None, + alias: None, } } } @@ -165,7 +169,8 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: self.name.clone(), + name: self.def.name(db)?.to_smol_str(), + alias: if self.is_alias { Some(self.name.clone()) } else { None }, kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, focus_range: Some(name_range.range), @@ -466,6 +471,7 @@ impl ToNav for LocalSource { NavigationTarget { file_id, name, + alias: None, kind: Some(kind), full_range, focus_range, @@ -494,6 +500,7 @@ impl ToNav for hir::Label { NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::Label), full_range, focus_range, @@ -534,6 +541,7 @@ impl TryToNav for hir::TypeParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::TypeParam), full_range, focus_range, @@ -560,6 +568,7 @@ impl TryToNav for hir::LifetimeParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::LifetimeParam), full_range, focus_range: Some(full_range), @@ -589,6 +598,7 @@ impl TryToNav for hir::ConstParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::ConstParam), full_range, focus_range, @@ -643,6 +653,7 @@ fn foo() { enum FooInner { } } focus_range: 34..42, name: "FooInner", kind: Enum, + container_name: "foo", description: "enum FooInner", }, ] diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 64150cc2f7fdb..ec57c02145eb8 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2232,14 +2232,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 52..115, - focus_range: 67..75, - name: "foo_test", + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo_test", + "tests::foo2_test", ), attr: TestAttr { ignore: false, @@ -2253,14 +2253,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 121..185, - focus_range: 136..145, - name: "foo2_test", + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo2_test", + "tests::foo_test", ), attr: TestAttr { ignore: false, diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 9c198eefc757f..c8eda567db798 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -520,7 +520,10 @@ pub(crate) fn handle_workspace_symbol( #[allow(deprecated)] let info = SymbolInformation { - name: nav.name.to_string(), + name: match nav.alias { + Some(ref alias) => format!("{} (alias {})", alias, nav.name), + None => format!("{}", nav.name), + }, kind: nav .kind .map(to_proto::symbol_kind) From 609f36f11f023a26a789f9b9c2a0dba3f04833bf Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 25 May 2023 13:03:17 +0300 Subject: [PATCH 495/806] deps: drop serde feature from url --- clippy_lints/Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1b818e8ab64df..01cefc32a4d87 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -26,9 +26,7 @@ unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" rustc-semver = "1.1" -# NOTE: cargo requires serde feat in its url dep -# see -url = { version = "2.2", features = ["serde"] } +url = "2.2" [features] deny-warnings = ["clippy_utils/deny-warnings"] From 066037b620b3ee72af25a88b626b991b4d522f31 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 25 May 2023 13:26:17 +0300 Subject: [PATCH 496/806] actually, remove rustc-workspace-hack dependency too --- Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c72bb62ed19e..257d067dcd84a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,11 +35,6 @@ walkdir = "2.3" # This is used by the `collect-metadata` alias. filetime = "0.2" -# A noop dependency that changes in the Rust repository, it's a bit of a hack. -# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` -# for more information. -rustc-workspace-hack = "1.0" - # UI test dependencies clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } From e3dfcf2eb2daabe777c7f320b37a4fe6c117d5b1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 25 May 2023 16:20:28 +0200 Subject: [PATCH 497/806] Add context to overly long loop message --- crates/rust-analyzer/src/main_loop.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f06abe0763e36..99aa48fd0798e 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -184,6 +184,7 @@ impl GlobalState { // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); + let event_dbg = format!("{event:?}"); tracing::debug!("{:?} handle_event({:?})", loop_start, event); let task_queue_len = self.task_pool.handle.len(); if task_queue_len > 0 { @@ -394,8 +395,10 @@ impl GlobalState { let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { - tracing::warn!("overly long loop turn: {:?}", loop_duration); - self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}")); + tracing::warn!("overly long loop turn took {loop_duration:?}: {event_dbg}"); + self.poke_rust_analyzer_developer(format!( + "overly long loop turn took {loop_duration:?}: {event_dbg}" + )); } Ok(()) } From b0f17668f736278c535f58189112a1539545e7cb Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 25 May 2023 18:46:34 +0330 Subject: [PATCH 498/806] use `::core` instead of `$crate` in `option_env!` --- .../macro_expansion_tests/builtin_fn_macro.rs | 2 +- crates/hir-expand/src/builtin_fn_macro.rs | 6 ++--- crates/hir-ty/src/tests/macros.rs | 25 ++++++++++++++----- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 13aa6130be8b6..006d292992179 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); } #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { $crate::option::Option::None:: < &str>; } +fn main() { ::core::option::Option::None:: < &str>; } "#]], ); } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 0640ba774bb04..7ce5b02314ee1 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -820,10 +820,10 @@ fn option_env_expand( ) } }; - + // FIXME: Use `DOLLAR_CRATE` when that works in eager macros. let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> }, - Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) }, + None => quote! { ::core::option::Option::None::<&str> }, + Some(s) => quote! { ::core::option::Option::Some(#s) }, }; ExpandResult::ok(ExpandedEager::new(expanded)) diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 9377a3a5f26ac..772bd3e536ece 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -947,7 +947,7 @@ fn infer_builtin_macros_concat_with_lazy() { #[test] fn infer_builtin_macros_env() { - check_infer( + check_types( r#" //- /main.rs env:foo=bar #[rustc_builtin_macro] @@ -955,13 +955,26 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); + //^ &str + } + "#, + ); +} + +#[test] +fn infer_builtin_macros_option_env() { + check_types( + r#" + //- minicore: option + //- /main.rs env:foo=bar + #[rustc_builtin_macro] + macro_rules! option_env {() => {}} + + fn main() { + let x = option_env!("foo"); + //^ Option<&str> } "#, - expect![[r#" - !0..22 '"__RA_...TED__"': &str - 62..90 '{ ...o"); }': () - 72..73 'x': &str - "#]], ); } From 7ef185d65ea8da681f0f89ab4785c45f25cfaf91 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 25 May 2023 19:37:04 +0330 Subject: [PATCH 499/806] evaluate `UnevaluatedConst` in unify --- crates/hir-ty/src/infer/expr.rs | 3 + crates/hir-ty/src/infer/unify.rs | 10 +++- crates/hir-ty/src/tests/regression.rs | 55 +++++++++++++++++++ .../src/handlers/type_mismatch.rs | 22 ++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index b800c2e32a9d5..7772fab796cc2 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -575,6 +575,9 @@ impl<'a> InferenceContext<'a> { let field_ty = field_def.map_or(self.err_ty(), |it| { field_types[it.local_id].clone().substitute(Interner, &substs) }); + // Field type might have some unknown types + // FIXME: we may want to emit a single type variable for all instance of type fields? + let field_ty = self.insert_type_vars(field_ty); self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); } if let Some(expr) = spread { diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 21b962a48f246..38eae475b556f 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -781,8 +781,16 @@ impl<'a> InferenceTable<'a> { pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { let data = c.data(Interner); match &data.value { - ConstValue::Concrete(cc) => match cc.interned { + ConstValue::Concrete(cc) => match &cc.interned { crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()), + // try to evaluate unevaluated const. Replace with new var if const eval failed. + crate::ConstScalar::UnevaluatedConst(id, subst) => { + if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + eval + } else { + self.new_const_var(data.ty.clone()) + } + } _ => c, }, _ => c, diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 9f5f1ea3255aa..259f43e7e201e 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1837,3 +1837,58 @@ fn foo() { }", ); } + +#[test] +fn regression_14844() { + check_no_mismatches( + r#" +pub type Ty = Unknown; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::(), + }; +} + "#, + ); + check_no_mismatches( + r#" +pub const ONE: usize = 1; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<1>(), + }; +} + "#, + ); + check_no_mismatches( + r#" +pub const ONE: usize = unknown(); + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<1>(), + }; +} + "#, + ); +} diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index a5359741ac950..cc282bf934812 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -644,6 +644,28 @@ fn h() { ); } + #[test] + fn evaluate_const_generics_in_types() { + check_diagnostics( + r#" +pub const ONE: usize = 1; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<2>(), + //^^^^^^^^^^^^ error: expected Inner<1>, found Inner<2> + }; +} +"#, + ); + } + #[test] fn type_mismatch_pat_smoke_test() { check_diagnostics( From 780349bdafb7983d59d07891abe4225e28d96541 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 26 May 2023 02:08:33 +0330 Subject: [PATCH 500/806] fix `need-mut` false positive in closure capture of match scrutinee --- crates/hir-def/src/body.rs | 20 ++++++++------ crates/hir-ty/src/infer/closure.rs | 26 +++++++++++++++---- crates/hir-ty/src/layout/tests/closure.rs | 14 +++++++++- .../src/handlers/mutability_errors.rs | 26 +++++++++++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index a387bdbc19eba..36626ed1a9b1f 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -227,9 +227,8 @@ impl Body { }); } - pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) { + pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) { let pat = &self[pat_id]; - f(pat_id); match pat { Pat::Range { .. } | Pat::Lit(..) @@ -239,23 +238,28 @@ impl Body { | Pat::Missing => {} &Pat::Bind { subpat, .. } => { if let Some(subpat) = subpat { - self.walk_pats(subpat, f); + f(subpat); } } Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { - args.iter().copied().for_each(|p| self.walk_pats(p, f)); + args.iter().copied().for_each(|p| f(p)); } - Pat::Ref { pat, .. } => self.walk_pats(*pat, f), + Pat::Ref { pat, .. } => f(*pat), Pat::Slice { prefix, slice, suffix } => { let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); - total_iter.copied().for_each(|p| self.walk_pats(p, f)); + total_iter.copied().for_each(|p| f(p)); } Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f)); + args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat)); } - Pat::Box { inner } => self.walk_pats(*inner, f), + Pat::Box { inner } => f(*inner), } } + + pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) { + f(pat_id); + self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f)); + } } impl Default for Body { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 7878ebcc58663..787c5c54a2916 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -643,7 +643,21 @@ impl InferenceContext<'_> { } None => *result = Some(ck), }; - self.body.walk_pats(pat, &mut |p| match &self.body[p] { + + self.walk_pat_inner( + pat, + &mut update_result, + BorrowKind::Mut { allow_two_phase_borrow: false }, + ); + } + + fn walk_pat_inner( + &mut self, + p: PatId, + update_result: &mut impl FnMut(CaptureKind), + mut for_mut: BorrowKind, + ) { + match &self.body[p] { Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing @@ -678,13 +692,15 @@ impl InferenceContext<'_> { } } crate::BindingMode::Ref(r) => match r { - Mutability::Mut => update_result(CaptureKind::ByRef(BorrowKind::Mut { - allow_two_phase_borrow: false, - })), + Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), }, }, - }); + } + if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) { + for_mut = BorrowKind::Unique; + } + self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); } fn expr_ty(&self, expr: ExprId) -> Ty { diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs index 811d60888074e..576e7f3fc619c 100644 --- a/crates/hir-ty/src/layout/tests/closure.rs +++ b/crates/hir-ty/src/layout/tests/closure.rs @@ -201,7 +201,7 @@ fn match_pattern() { ] |x: i64| { match y { - X(_a, _b, _c) => x, + X(_a, _, _c) => x, } } } @@ -217,6 +217,18 @@ fn match_pattern() { } } } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + ref _y => x, + } + } + } } #[test] diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 0af4100180826..45b44c2c5ca91 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -352,6 +352,32 @@ fn main() { ); } + #[test] + fn match_closure_capture() { + check_diagnostics( + r#" +//- minicore: option +fn main() { + let mut v = &mut Some(2); + //^^^^^ 💡 weak: variable does not need to be mutable + let _ = || match v { + Some(k) => { + *k = 5; + } + None => {} + }; + let v = &mut Some(2); + let _ = || match v { + //^ 💡 error: cannot mutate immutable variable `v` + ref mut k => { + *k = &mut Some(5); + } + }; +} +"#, + ); + } + #[test] fn match_bindings() { check_diagnostics( From 5b0e170683cb8eccfbbb1270c21a1aacedf0ecee Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 25 May 2023 15:47:34 -0700 Subject: [PATCH 501/806] Allow users to override the .scip output file path Previously, rust-analyzer would write to the file index.scip unconditionally. --- crates/rust-analyzer/src/cli/flags.rs | 4 ++++ crates/rust-analyzer/src/cli/scip.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 6b5a79b431f18..31012c01b9530 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -112,6 +112,9 @@ xflags::xflags! { cmd scip { required path: PathBuf + + /// The output path where the SCIP file will be written to. Defaults to `index.scip`. + optional --output path: PathBuf } } } @@ -208,6 +211,7 @@ pub struct Lsif { #[derive(Debug)] pub struct Scip { pub path: PathBuf, + pub output: Option, } impl RustAnalyzer { diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 61924d5843193..b0b724bdfe73d 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -2,6 +2,7 @@ use std::{ collections::{HashMap, HashSet}, + path::PathBuf, time::Instant, }; @@ -165,7 +166,8 @@ impl flags::Scip { special_fields: Default::default(), }; - scip::write_message_to_file("index.scip", index) + let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip")); + scip::write_message_to_file(out_path, index) .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?; eprintln!("Generating SCIP finished {:?}", now.elapsed()); From ec33e6414cc39a34d2c49f6efdf933ca21a0136c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Thu, 25 May 2023 23:32:07 +0000 Subject: [PATCH 502/806] bootstrap: Make `clean` respect `dry-run` --- src/bootstrap/clean.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 0d9fd56b03814..c1d867a0bd14b 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -85,6 +85,10 @@ clean_crate_tree! { } fn clean_default(build: &Build, all: bool) { + if build.config.dry_run() { + return; + } + rm_rf("tmp".as_ref()); if all { From b42ff20c560fb53982b3f77d3fed5c459a01b545 Mon Sep 17 00:00:00 2001 From: jyn Date: Thu, 25 May 2023 23:56:15 -0500 Subject: [PATCH 503/806] Revert "Enable incremental independent of stage" This reverts commit 827f656ebb1230f31af6d968c4bfe69a79914ffc. Incremental is not sound to use across stages. Arbitrary changes to the compiler can invalidate the incremental cache - even changes to normal queries, not incremental itself! - and we do not currently enable `incremental-verify-ich` in bootstrap. Since 2018, we highly recommend and nudge users towards stage 1 builds instead of stage 2, and using `keep-stage` for anything other than libstd is very rare. I don't think the risk of unsoundness is worth the very minor speedup when building libstd. Disable incremental to avoid spurious panics and miscompilations when building with the stage 1 and 2 sysroot. --- src/bootstrap/builder.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index cf7c6596c0238..685824278ff25 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1783,7 +1783,10 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); } - if self.config.incremental { + // Ignore incremental modes except for stage0, since we're + // not guaranteeing correctness across builds if the compiler + // is changing under your feet. + if self.config.incremental && compiler.stage == 0 { cargo.env("CARGO_INCREMENTAL", "1"); } else { // Don't rely on any default setting for incr. comp. in Cargo From 397c8e51480cc6c350433deefd8548e1455506c6 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Fri, 26 May 2023 15:50:56 +0900 Subject: [PATCH 504/806] fix: don't try determining type of token inside macro calls --- crates/ide/src/goto_type_definition.rs | 87 ++++++++++++++++++-------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index fb55a60ec43bc..6048990f7492c 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition( }; let range = token.text_range(); sema.descend_into_macros(token) - .iter() + .into_iter() .filter_map(|token| { - let ty = sema.token_ancestors_with_macros(token.clone()).find_map(|node| { - let ty = match_ast! { - match node { - ast::Expr(it) => sema.type_of_expr(&it)?.original, - ast::Pat(it) => sema.type_of_pat(&it)?.original, - ast::SelfParam(it) => sema.type_of_self(&it)?, - ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, - // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise - ast::NameRef(it) => { - if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { - let (_, _, ty) = sema.resolve_record_field(&record_field)?; - ty - } else { - let record_field = ast::RecordPatField::for_field_name_ref(&it)?; - sema.resolve_record_pat_field(&record_field)?.1 - } - }, - _ => return None, - } - }; + let ty = sema + .token_ancestors_with_macros(token) + // When `token` is within a macro call, we can't determine its type. Don't continue + // this traversal because otherwise we'll end up returning the type of *that* macro + // call, which is not what we want in general. + // + // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test + // if the current node is a `TokenTree`. + .take_while(|node| !ast::TokenTree::can_cast(node.kind())) + .find_map(|node| { + let ty = match_ast! { + match node { + ast::Expr(it) => sema.type_of_expr(&it)?.original, + ast::Pat(it) => sema.type_of_pat(&it)?.original, + ast::SelfParam(it) => sema.type_of_self(&it)?, + ast::Type(it) => sema.resolve_type(&it)?, + ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()), + // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise + ast::NameRef(it) => { + if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { + let (_, _, ty) = sema.resolve_record_field(&record_field)?; + ty + } else { + let record_field = ast::RecordPatField::for_field_name_ref(&it)?; + sema.resolve_record_pat_field(&record_field)?.1 + } + }, + _ => return None, + } + }; - Some(ty) - }); + Some(ty) + }); ty }) .for_each(|ty| { @@ -94,7 +103,7 @@ mod tests { fn check(ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); let navs = analysis.goto_type_definition(position).unwrap().unwrap().info; - assert_ne!(navs.len(), 0); + assert!(!navs.is_empty(), "navigation is empty"); let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start()); let navs = navs @@ -104,7 +113,7 @@ mod tests { .collect::>(); let expected = expected .into_iter() - .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range }) + .map(|(file_range, _)| file_range) .sorted_by_key(cmp) .collect::>(); assert_eq!(expected, navs); @@ -198,6 +207,32 @@ id! { ); } + #[test] + fn dont_collect_type_from_token_in_macro_call() { + check( + r#" +struct DontCollectMe; +struct S; + //^ + +macro_rules! inner { + ($t:tt) => { DontCollectMe } +} +macro_rules! m { + ($t:ident) => { + match $t { + _ => inner!($t); + } + } +} + +fn test() { + m!($0S); +} +"#, + ); + } + #[test] fn goto_type_definition_for_param() { check( From c21d09f3ccb161880033aef699734ddcf84c59d7 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 26 May 2023 14:26:13 +0330 Subject: [PATCH 505/806] insert type vars in function arguments --- crates/hir-ty/src/infer/expr.rs | 10 ++++++---- crates/hir-ty/src/tests/regression.rs | 20 +++++++++++++++++++ .../src/handlers/type_mismatch.rs | 19 ++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 7772fab796cc2..f30758484300f 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1025,7 +1025,8 @@ impl<'a> InferenceContext<'a> { ) } }; - + // Try to evaluate unevaluated constant, and insert variable if is not possible. + let len = self.table.insert_const_vars_shallow(len); TyKind::Array(elem_ty, len).intern(Interner) } @@ -1681,9 +1682,10 @@ impl<'a> InferenceContext<'a> { } else { param_ty }; - if !coercion_target.is_unknown() - && self.coerce(Some(arg), &ty, &coercion_target).is_err() - { + // The function signature may contain some unknown types, so we need to insert + // type vars here to avoid type mismatch false positive. + let coercion_target = self.insert_type_vars(coercion_target); + if self.coerce(Some(arg), &ty, &coercion_target).is_err() { self.result.type_mismatches.insert( arg.into(), TypeMismatch { expected: coercion_target, actual: ty.clone() }, diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 259f43e7e201e..1fdeddede096c 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1888,6 +1888,26 @@ fn main() { _ = Outer { inner: Inner::<1>(), }; +} + "#, + ); + check_no_mismatches( + r#" +pub const N: usize = 2 + 2; + +fn f(t: [u8; N]) {} + +fn main() { + let a = [1, 2, 3, 4]; + f(a); + let b = [1; 4]; + let c: [u8; N] = b; + let d = [1; N]; + let e: [u8; N] = d; + let f = [1; N]; + let g = match f { + [a, b, c, d] => a + b + c + d, + }; } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index cc282bf934812..c28f98d8333e2 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -644,6 +644,25 @@ fn h() { ); } + #[test] + fn unknown_type_in_function_signature() { + check_diagnostics( + r#" +struct X(T); + +fn foo(x: X) {} +fn test1() { + // Unknown might be `i32`, so we should not emit type mismatch here. + foo(X(42)); +} +fn test2() { + foo(42); + //^^ error: expected X<{unknown}>, found i32 +} +"#, + ); + } + #[test] fn evaluate_const_generics_in_types() { check_diagnostics( From 59f8827a6fbd635d5314bec0f88284e8f3c603fd Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Wed, 26 Apr 2023 16:13:58 +0200 Subject: [PATCH 506/806] Implement assist to replace named generic with impl This adds a new assist named "replace named generic with impl" to move the generic param type from the generic param list into the function signature. ```rust fn new(input: T) -> Self {} ``` becomes ```rust fn new(input: impl ToString) -> Self {} ``` The first step is to determine if the assist can be applied, there has to be a match between generic trait param & function paramter types. * replace function parameter type(s) with impl * add new `impl_trait_type` function to generate the new trait bounds with `impl` keyword for use in the function signature --- .../replace_named_generic_with_impl.rs | 143 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 13 ++ crates/syntax/src/ast/make.rs | 4 + 4 files changed, 162 insertions(+) create mode 100644 crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs new file mode 100644 index 0000000000000..bd73c7b9c9dd5 --- /dev/null +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -0,0 +1,143 @@ +use syntax::{ + ast::{ + self, + make::{self, impl_trait_type}, + HasGenericParams, HasName, HasTypeBounds, + }, + ted, AstNode, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: replace_named_generic_with_impl +// +// Replaces named generic with an `impl Trait` in function argument. +// +// ``` +// fn new>(location: P) -> Self {} +// ``` +// -> +// ``` +// fn new(location: impl AsRef) -> Self {} +// ``` +pub(crate) fn replace_named_generic_with_impl( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + // finds `>` + let type_param = ctx.find_node_at_offset::()?; + + // The list of type bounds / traits for generic name `P` + let type_bound_list = type_param.type_bound_list()?; + + // returns `P` + let type_param_name = type_param.name()?; + + let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; + let params = fn_ + .param_list()? + .params() + .filter_map(|param| { + // function parameter type needs to match generic type name + if let ast::Type::PathType(path_type) = param.ty()? { + let left = path_type.path()?.segment()?.name_ref()?.ident_token()?.to_string(); + let right = type_param_name.to_string(); + if left == right { + Some(param) + } else { + None + } + } else { + None + } + }) + .collect::>(); + + if params.is_empty() { + return None; + } + + let target = type_param.syntax().text_range(); + + acc.add( + AssistId("replace_named_generic_with_impl", AssistKind::RefactorRewrite), + "Replace named generic with impl", + target, + |edit| { + let type_param = edit.make_mut(type_param); + let fn_ = edit.make_mut(fn_); + + // Replace generic type in `>` to `

` + let new_ty = make::ty(&type_param_name.to_string()).clone_for_update(); + ted::replace(type_param.syntax(), new_ty.syntax()); + + if let Some(generic_params) = fn_.generic_param_list() { + if generic_params.generic_params().count() == 0 { + ted::remove(generic_params.syntax()); + } + } + + // Replace generic type parameter: `foo(p: P)` -> `foo(p: impl AsRef)` + let new_bounds = impl_trait_type(type_bound_list).clone_for_update(); + + for param in params { + if let Some(ast::Type::PathType(param_type)) = param.ty() { + let param_type = edit.make_mut(param_type).clone_for_update(); + ted::replace(param_type.syntax(), new_bounds.syntax()); + } + } + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::check_assist; + + #[test] + fn replace_generic_moves_into_function() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(input: T) -> Self {}"#, + r#"fn new(input: impl ToString) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_with_inner_associated_type() { + check_assist( + replace_named_generic_with_impl, + r#"fn new>(input: P) -> Self {}"#, + r#"fn new(input: impl AsRef) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_trait_applies_to_all_matching_params() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(a: T, b: T) -> Self {}"#, + r#"fn new(a: impl ToString, b: impl ToString) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_with_multiple_generic_names() { + check_assist( + replace_named_generic_with_impl, + r#"fn new, T$0: ToString>(t: T, p: P) -> Self {}"#, + r#"fn new>(t: impl ToString, p: P) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_with_multiple_trait_bounds() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(p: P) -> Self {}"#, + r#"fn new(p: impl Send + Sync) -> Self {}"#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index fc03903e593fc..bd282e5343409 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -193,6 +193,7 @@ mod handlers { mod replace_arith_op; mod introduce_named_generic; mod replace_let_with_if_let; + mod replace_named_generic_with_impl; mod replace_qualified_name_with_use; mod replace_string_with_char; mod replace_turbofish_with_explicit_type; @@ -299,6 +300,7 @@ mod handlers { replace_let_with_if_let::replace_let_with_if_let, replace_method_eager_lazy::replace_with_eager_method, replace_method_eager_lazy::replace_with_lazy_method, + replace_named_generic_with_impl::replace_named_generic_with_impl, 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/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 0096254ecb781..8a35fd290e686 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -2338,6 +2338,19 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_replace_named_generic_with_impl() { + check_doc_test( + "replace_named_generic_with_impl", + r#####" +fn new>(location: P) -> Self {} +"#####, + r#####" +fn new(location: impl AsRef) -> Self {} +"#####, + ) +} + #[test] fn doctest_replace_qualified_name_with_use() { check_doc_test( diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 8d2dc8709bafd..3a61fb0a52c26 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -232,6 +232,10 @@ pub fn impl_trait( ast_from_text(&format!("impl{ty_params_str} {trait_} for {ty}{ty_genargs_str} {{}}")) } +pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType { + ast_from_text(&format!("fn f(x: impl {bounds}) {{}}")) +} + pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { ast_from_text(&format!("type __ = {name_ref};")) } From 95f59668e65d53a985897af1c7ae4c2b449016ba Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Mon, 15 May 2023 17:38:25 +0200 Subject: [PATCH 507/806] Fix removal of generic param from list This removes an existing generic param from the `GenericParamList`. It also considers to remove the extra colon & whitespace to the previous sibling. * change order to get all param types first and mark them as mutable before the first edit happens * add helper function to remove a generic parameter * fix test output --- .../replace_named_generic_with_impl.rs | 32 +++++++++---------- crates/syntax/src/ast/edit_in_place.rs | 15 +++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index bd73c7b9c9dd5..f658ba768d8ab 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -1,9 +1,5 @@ use syntax::{ - ast::{ - self, - make::{self, impl_trait_type}, - HasGenericParams, HasName, HasTypeBounds, - }, + ast::{self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds}, ted, AstNode, }; @@ -27,7 +23,7 @@ pub(crate) fn replace_named_generic_with_impl( // finds `>` let type_param = ctx.find_node_at_offset::()?; - // The list of type bounds / traits for generic name `P` + // The list of type bounds / traits: `AsRef` let type_bound_list = type_param.type_bound_list()?; // returns `P` @@ -67,24 +63,26 @@ pub(crate) fn replace_named_generic_with_impl( let type_param = edit.make_mut(type_param); let fn_ = edit.make_mut(fn_); - // Replace generic type in `>` to `

` - let new_ty = make::ty(&type_param_name.to_string()).clone_for_update(); - ted::replace(type_param.syntax(), new_ty.syntax()); + // get all params + let param_types = params + .iter() + .filter_map(|param| match param.ty() { + Some(ast::Type::PathType(param_type)) => Some(edit.make_mut(param_type)), + _ => None, + }) + .collect::>(); if let Some(generic_params) = fn_.generic_param_list() { + generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); if generic_params.generic_params().count() == 0 { ted::remove(generic_params.syntax()); } } - // Replace generic type parameter: `foo(p: P)` -> `foo(p: impl AsRef)` - let new_bounds = impl_trait_type(type_bound_list).clone_for_update(); - - for param in params { - if let Some(ast::Type::PathType(param_type)) = param.ty() { - let param_type = edit.make_mut(param_type).clone_for_update(); - ted::replace(param_type.syntax(), new_bounds.syntax()); - } + // get type bounds in signature type: `P` -> `impl AsRef` + let new_bounds = impl_trait_type(type_bound_list); + for param_type in param_types.iter().rev() { + ted::replace(param_type.syntax(), new_bounds.clone_for_update().syntax()); } }, ) diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 995e8d8d15290..b3ea6ca8d46a1 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -236,6 +236,21 @@ impl ast::GenericParamList { } } + /// Removes the existing generic param + pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { + if let Some(previous) = generic_param.syntax().prev_sibling() { + if let Some(next_token) = previous.next_sibling_or_token() { + ted::remove_all(next_token..=generic_param.syntax().clone().into()); + } + } else if let Some(next) = generic_param.syntax().next_sibling() { + if let Some(next_token) = next.prev_sibling_or_token() { + ted::remove_all(generic_param.syntax().clone().into()..=next_token); + } + } else { + ted::remove(generic_param.syntax()); + } + } + /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { From ce1c85317f80b96b5fdc65fa2e76d4150f26abfa Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Fri, 26 May 2023 11:59:44 +0200 Subject: [PATCH 508/806] Check param is not referenced in function This checks the type param is referenced neither in the function body nor as a return type. * add tests --- .../replace_named_generic_with_impl.rs | 87 +++++++++++++++++-- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index f658ba768d8ab..4e7011b5ca385 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -1,3 +1,10 @@ +use hir::{Semantics, TypeParam}; +use ide_db::{ + base_db::{FileId, FileRange}, + defs::Definition, + search::SearchScope, + RootDatabase, +}; use syntax::{ ast::{self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds}, ted, AstNode, @@ -22,13 +29,12 @@ pub(crate) fn replace_named_generic_with_impl( ) -> Option<()> { // finds `>` let type_param = ctx.find_node_at_offset::()?; + // returns `P` + let type_param_name = type_param.name()?; // The list of type bounds / traits: `AsRef` let type_bound_list = type_param.type_bound_list()?; - // returns `P` - let type_param_name = type_param.name()?; - let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; let params = fn_ .param_list()? @@ -53,6 +59,11 @@ pub(crate) fn replace_named_generic_with_impl( return None; } + let type_param_hir_def = ctx.sema.to_def(&type_param)?; + if is_referenced_outside(ctx.db(), type_param_hir_def, &fn_, ctx.file_id()) { + return None; + } + let target = type_param.syntax().text_range(); acc.add( @@ -88,11 +99,36 @@ pub(crate) fn replace_named_generic_with_impl( ) } +fn is_referenced_outside( + db: &RootDatabase, + type_param: TypeParam, + fn_: &ast::Fn, + file_id: FileId, +) -> bool { + let semantics = Semantics::new(db); + let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param)); + + // limit search scope to function body & return type + let search_ranges = vec![ + fn_.body().map(|body| body.syntax().text_range()), + fn_.ret_type().map(|ret_type| ret_type.syntax().text_range()), + ]; + + search_ranges.into_iter().filter_map(|search_range| search_range).any(|search_range| { + let file_range = FileRange { file_id, range: search_range }; + !type_param_def + .usages(&semantics) + .in_scope(SearchScope::file_range(file_range)) + .all() + .is_empty() + }) +} + #[cfg(test)] mod tests { use super::*; - use crate::tests::check_assist; + use crate::tests::{check_assist, check_assist_not_applicable}; #[test] fn replace_generic_moves_into_function() { @@ -122,12 +158,22 @@ mod tests { } #[test] - fn replace_generic_with_multiple_generic_names() { + fn replace_generic_with_multiple_generic_params() { check_assist( replace_named_generic_with_impl, r#"fn new, T$0: ToString>(t: T, p: P) -> Self {}"#, r#"fn new>(t: impl ToString, p: P) -> Self {}"#, ); + check_assist( + replace_named_generic_with_impl, + r#"fn new>(t: T, p: P) -> Self {}"#, + r#"fn new>(t: impl ToString, p: P) -> Self {}"#, + ); + check_assist( + replace_named_generic_with_impl, + r#"fn new(a: A, b: B, c: C) -> Self {}"#, + r#"fn new(a: A, b: impl ToString, c: C) -> Self {}"#, + ); } #[test] @@ -138,4 +184,35 @@ mod tests { r#"fn new(p: impl Send + Sync) -> Self {}"#, ); } + + #[test] + fn replace_generic_not_applicable_if_param_used_as_return_type() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn new(p: P) -> P {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_if_param_used_in_fn_body() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn new(p: P) { let x: &dyn P = &O; }"#, + ); + } + + #[test] + fn replace_generic_ignores_another_function_with_same_param_type() { + check_assist( + replace_named_generic_with_impl, + r#" + fn new(p: P) {} + fn hello(p: P) { println!("{:?}", p); } + "#, + r#" + fn new(p: impl Send + Sync) {} + fn hello(p: P) { println!("{:?}", p); } + "#, + ); + } } From d49924dc6e3c236e305c28cb630d5f2888a7e363 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Fri, 26 May 2023 13:24:44 +0200 Subject: [PATCH 509/806] Choose & over ref, make nav target's name more intuitive. --- crates/ide/src/navigation_target.rs | 2 +- crates/rust-analyzer/src/handlers/request.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 65c37ca68c522..7ec7788a245b0 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -169,7 +169,7 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: self.def.name(db)?.to_smol_str(), + name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() }, alias: if self.is_alias { Some(self.name.clone()) } else { None }, kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index c8eda567db798..eabc39b3e09cd 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -520,8 +520,8 @@ pub(crate) fn handle_workspace_symbol( #[allow(deprecated)] let info = SymbolInformation { - name: match nav.alias { - Some(ref alias) => format!("{} (alias {})", alias, nav.name), + name: match &nav.alias { + Some(alias) => format!("{} (alias for {})", alias, nav.name), None => format!("{}", nav.name), }, kind: nav From e78df83e2fb89b475187f7d4b631a68619b590b5 Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Fri, 26 May 2023 13:43:12 +0200 Subject: [PATCH 510/806] Integrate feedback * pass in existing `Semantics` object to function * pass in `Definition` for param type * refactor iterator to use `flatten` --- .../replace_named_generic_with_impl.rs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 4e7011b5ca385..f32ceb42654bb 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -1,4 +1,4 @@ -use hir::{Semantics, TypeParam}; +use hir::Semantics; use ide_db::{ base_db::{FileId, FileRange}, defs::Definition, @@ -60,7 +60,9 @@ pub(crate) fn replace_named_generic_with_impl( } let type_param_hir_def = ctx.sema.to_def(&type_param)?; - if is_referenced_outside(ctx.db(), type_param_hir_def, &fn_, ctx.file_id()) { + let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param_hir_def)); + + if is_referenced_outside(&ctx.sema, type_param_def, &fn_, ctx.file_id()) { return None; } @@ -100,27 +102,20 @@ pub(crate) fn replace_named_generic_with_impl( } fn is_referenced_outside( - db: &RootDatabase, - type_param: TypeParam, + sema: &Semantics<'_, RootDatabase>, + type_param_def: Definition, fn_: &ast::Fn, file_id: FileId, ) -> bool { - let semantics = Semantics::new(db); - let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param)); - // limit search scope to function body & return type let search_ranges = vec![ fn_.body().map(|body| body.syntax().text_range()), fn_.ret_type().map(|ret_type| ret_type.syntax().text_range()), ]; - search_ranges.into_iter().filter_map(|search_range| search_range).any(|search_range| { + search_ranges.into_iter().flatten().any(|search_range| { let file_range = FileRange { file_id, range: search_range }; - !type_param_def - .usages(&semantics) - .in_scope(SearchScope::file_range(file_range)) - .all() - .is_empty() + !type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all().is_empty() }) } From 79fe11ced3283b88c4a24da972e1649eb16a9f1b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 15:09:19 +0200 Subject: [PATCH 511/806] Shuffle some things around --- crates/rust-analyzer/src/global_state.rs | 14 ++- crates/rust-analyzer/src/main_loop.rs | 151 ++++++++++++----------- 2 files changed, 91 insertions(+), 74 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 08431d6488207..defb3e1461fa5 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -52,24 +52,31 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; pub(crate) struct GlobalState { sender: Sender, req_queue: ReqQueue, + pub(crate) task_pool: Handle, Receiver>, - pub(crate) loader: Handle, Receiver>, + pub(crate) config: Arc, pub(crate) analysis_host: AnalysisHost, pub(crate) diagnostics: DiagnosticCollection, pub(crate) mem_docs: MemDocs, + pub(crate) source_root_config: SourceRootConfig, pub(crate) semantic_tokens_cache: Arc>>, + + // status pub(crate) shutdown_requested: bool, pub(crate) last_reported_status: Option, - pub(crate) source_root_config: SourceRootConfig, + // proc macros pub(crate) proc_macro_changed: bool, pub(crate) proc_macro_clients: Arc<[anyhow::Result]>, + // Flycheck pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, + // VFS + pub(crate) loader: Handle, Receiver>, pub(crate) vfs: Arc)>>, pub(crate) vfs_config_version: u32, pub(crate) vfs_progress_config_version: u32, @@ -102,11 +109,12 @@ pub(crate) struct GlobalState { /// the user just adds comments or whitespace to Cargo.toml, we do not want /// to invalidate any salsa caches. pub(crate) workspaces: Arc>, + + // op queues pub(crate) fetch_workspaces_queue: OpQueue<(), Option>>>, pub(crate) fetch_build_data_queue: OpQueue<(), (Arc>, Vec>)>, pub(crate) fetch_proc_macros_queue: OpQueue, bool>, - pub(crate) prime_caches_queue: OpQueue, } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 99aa48fd0798e..b3522394cd60d 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -77,7 +77,7 @@ pub(crate) enum PrimeCachesProgress { impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter<'_>| { + let debug_non_verbose = |not: &Notification, f: &mut fmt::Formatter<'_>| { f.debug_struct("Notification").field("method", ¬.method).finish() }; @@ -86,7 +86,7 @@ impl fmt::Debug for Event { if notification_is::(not) || notification_is::(not) { - return debug_verbose_not(not, f); + return debug_non_verbose(not, f); } } Event::Task(Task::Response(resp)) => { @@ -112,38 +112,7 @@ impl GlobalState { self.update_status_or_notify(); if self.config.did_save_text_document_dynamic_registration() { - let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { - include_text: Some(false), - text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { - document_selector: Some(vec![ - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/*.rs".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.toml".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.lock".into()), - }, - ]), - }, - }; - - let registration = lsp_types::Registration { - id: "textDocument/didSave".to_string(), - method: "textDocument/didSave".to_string(), - register_options: Some(serde_json::to_value(save_registration_options).unwrap()), - }; - self.send_request::( - lsp_types::RegistrationParams { registrations: vec![registration] }, - |_, _| (), - ); + self.register_did_save_capability(); } self.fetch_workspaces_queue.request_op("startup".to_string(), ()); @@ -152,17 +121,54 @@ impl GlobalState { } while let Some(event) = self.next_event(&inbox) { - if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { - if not.method == lsp_types::notification::Exit::METHOD { - return Ok(()); - } + if matches!( + &event, + Event::Lsp(lsp_server::Message::Notification(Notification { method, .. })) + if method == lsp_types::notification::Exit::METHOD + ) { + return Ok(()); } - self.handle_event(event)? + self.handle_event(event)?; } Err("client exited without proper shutdown sequence".into()) } + fn register_did_save_capability(&mut self) { + let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { + include_text: Some(false), + text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { + document_selector: Some(vec![ + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/*.rs".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.toml".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.lock".into()), + }, + ]), + }, + }; + + let registration = lsp_types::Registration { + id: "textDocument/didSave".to_string(), + method: "textDocument/didSave".to_string(), + register_options: Some(serde_json::to_value(save_registration_options).unwrap()), + }; + self.send_request::( + lsp_types::RegistrationParams { registrations: vec![registration] }, + |_, _| (), + ); + } + fn next_event(&self, inbox: &Receiver) -> Option { select! { recv(inbox) -> msg => @@ -184,20 +190,20 @@ impl GlobalState { // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); - let event_dbg = format!("{event:?}"); - tracing::debug!("{:?} handle_event({:?})", loop_start, event); - let task_queue_len = self.task_pool.handle.len(); - if task_queue_len > 0 { - tracing::info!("task queue len: {}", task_queue_len); + let event_dbg_msg = format!("{event:?}"); + tracing::debug!("{:?} handle_event({})", loop_start, event_dbg_msg); + if tracing::enabled!(tracing::Level::INFO) { + let task_queue_len = self.task_pool.handle.len(); + if task_queue_len > 0 { + tracing::info!("task queue len: {}", task_queue_len); + } } let was_quiescent = self.is_quiescent(); match event { Event::Lsp(msg) => match msg { lsp_server::Message::Request(req) => self.on_new_request(loop_start, req), - lsp_server::Message::Notification(not) => { - self.on_notification(not)?; - } + lsp_server::Message::Notification(not) => self.on_notification(not)?, lsp_server::Message::Response(resp) => self.complete_request(resp), }, Event::Task(task) => { @@ -291,7 +297,8 @@ impl GlobalState { } } - if !was_quiescent || state_changed { + let client_refresh = !was_quiescent || state_changed; + if client_refresh { // Refresh semantic tokens if the client supports it. if self.config.semantic_tokens_refresh() { self.semantic_tokens_cache.lock().clear(); @@ -309,9 +316,9 @@ impl GlobalState { } } - if (!was_quiescent || state_changed || memdocs_added_or_removed) - && self.config.publish_diagnostics() - { + let update_diagnostics = (!was_quiescent || state_changed || memdocs_added_or_removed) + && self.config.publish_diagnostics(); + if update_diagnostics { self.update_diagnostics() } } @@ -371,38 +378,40 @@ impl GlobalState { } if let Some((cause, ())) = self.prime_caches_queue.should_start_op() { - tracing::debug!(%cause, "will prime caches"); - let num_worker_threads = self.config.prime_caches_num_threads(); - - self.task_pool.handle.spawn_with_sender({ - let analysis = self.snapshot().analysis; - move |sender| { - sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); - let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { - let report = PrimeCachesProgress::Report(progress); - sender.send(Task::PrimeCaches(report)).unwrap(); - }); - sender - .send(Task::PrimeCaches(PrimeCachesProgress::End { - cancelled: res.is_err(), - })) - .unwrap(); - } - }); + self.prime_caches(cause); } self.update_status_or_notify(); let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { - tracing::warn!("overly long loop turn took {loop_duration:?}: {event_dbg}"); + tracing::warn!("overly long loop turn took {loop_duration:?}: {event_dbg_msg}"); self.poke_rust_analyzer_developer(format!( - "overly long loop turn took {loop_duration:?}: {event_dbg}" + "overly long loop turn took {loop_duration:?}: {event_dbg_msg}" )); } Ok(()) } + fn prime_caches(&mut self, cause: String) { + tracing::debug!(%cause, "will prime caches"); + let num_worker_threads = self.config.prime_caches_num_threads(); + + self.task_pool.handle.spawn_with_sender({ + let analysis = self.snapshot().analysis; + move |sender| { + sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); + let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { + let report = PrimeCachesProgress::Report(progress); + sender.send(Task::PrimeCaches(report)).unwrap(); + }); + sender + .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() })) + .unwrap(); + } + }); + } + fn update_status_or_notify(&mut self) { let status = self.current_status(); if self.last_reported_status.as_ref() != Some(&status) { From a2b59b110fc417ec90b7f24882a416dd8a8fe618 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 15:21:00 +0200 Subject: [PATCH 512/806] Report config errors via status --- crates/rust-analyzer/src/config.rs | 10 +++++----- crates/rust-analyzer/src/global_state.rs | 4 +++- crates/rust-analyzer/src/handlers/notification.rs | 8 +------- crates/rust-analyzer/src/main_loop.rs | 6 +++++- crates/rust-analyzer/src/reload.rs | 5 +++++ 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index e73a2d5c7705c..05567f8f579cf 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -720,11 +720,11 @@ pub struct ClientCommandsConfig { } #[derive(Debug)] -pub struct ConfigUpdateError { +pub struct ConfigError { errors: Vec<(String, serde_json::Error)>, } -impl fmt::Display for ConfigUpdateError { +impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let errors = self.errors.iter().format_with("\n", |(key, e), f| { f(key)?; @@ -733,7 +733,7 @@ impl fmt::Display for ConfigUpdateError { }); write!( f, - "rust-analyzer found {} invalid config value{}:\n{}", + "invalid config value{}:\n{}", self.errors.len(), if self.errors.len() == 1 { "" } else { "s" }, errors @@ -777,7 +777,7 @@ impl Config { self.workspace_roots.extend(paths); } - pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> { + pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError> { tracing::info!("updating config from JSON: {:#}", json); if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { return Ok(()); @@ -824,7 +824,7 @@ impl Config { if errors.is_empty() { Ok(()) } else { - Err(ConfigUpdateError { errors }) + Err(ConfigError { errors }) } } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index defb3e1461fa5..d68e51240b79d 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -19,7 +19,7 @@ use triomphe::Arc; use vfs::AnchoredPathBuf; use crate::{ - config::Config, + config::{Config, ConfigError}, diagnostics::{CheckFixes, DiagnosticCollection}, from_proto, line_index::{LineEndings, LineIndex}, @@ -56,6 +56,7 @@ pub(crate) struct GlobalState { pub(crate) task_pool: Handle, Receiver>, pub(crate) config: Arc, + pub(crate) config_errors: Option, pub(crate) analysis_host: AnalysisHost, pub(crate) diagnostics: DiagnosticCollection, pub(crate) mem_docs: MemDocs, @@ -168,6 +169,7 @@ impl GlobalState { shutdown_requested: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), + config_errors: Default::default(), proc_macro_changed: false, // FIXME: use `Arc::from_iter` when it becomes available diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index ca9ea77f305c2..481004988d3ed 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -169,13 +169,7 @@ pub(crate) fn handle_did_change_configuration( // Note that json can be null according to the spec if the client can't // provide a configuration. This is handled in Config::update below. let mut config = Config::clone(&*this.config); - if let Err(error) = config.update(json.take()) { - this.show_message( - lsp_types::MessageType::WARNING, - error.to_string(), - false, - ); - } + config.update(json.take()); this.update_configuration(config); } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index b3522394cd60d..49595a45ea9de 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -419,7 +419,11 @@ impl GlobalState { if self.config.server_status_notification() { self.send_notification::(status); - } else if let (health, Some(message)) = (status.health, &status.message) { + } else if let ( + health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error), + Some(message), + ) = (status.health, &status.message) + { let open_log_button = tracing::enabled!(tracing::Level::ERROR) && (self.fetch_build_data_error().is_err() || self.fetch_workspace_error().is_err()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 4c76392a0e7f5..5300349e7d2ce 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -27,6 +27,7 @@ use ide_db::{ use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; +use stdx::format_to; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; @@ -134,6 +135,10 @@ impl GlobalState { message.push_str("Failed to discover workspace.\n"); message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n"); } + if let Some(err) = &self.config_errors { + status.health = lsp_ext::Health::Warning; + format_to!(message, "{err}\n"); + } for ws in self.workspaces.iter() { let (ProjectWorkspace::Cargo { sysroot, .. } From f876adf617622c36586a575960627a6f6cde0335 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 15:37:41 +0200 Subject: [PATCH 513/806] Report flycheck errors via status --- crates/rust-analyzer/src/config.rs | 1 - crates/rust-analyzer/src/global_state.rs | 2 ++ .../src/handlers/notification.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 19 ++++++++----------- crates/rust-analyzer/src/reload.rs | 5 +++++ 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 05567f8f579cf..b1d019cb113c7 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -734,7 +734,6 @@ impl fmt::Display for ConfigError { write!( f, "invalid config value{}:\n{}", - self.errors.len(), if self.errors.len() == 1 { "" } else { "s" }, errors ) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index d68e51240b79d..9f4dc4440202d 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -75,6 +75,7 @@ pub(crate) struct GlobalState { pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, + pub(crate) last_flycheck_error: Option, // VFS pub(crate) loader: Handle, Receiver>, @@ -179,6 +180,7 @@ impl GlobalState { flycheck: Arc::from(Vec::new()), flycheck_sender, flycheck_receiver, + last_flycheck_error: None, vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))), vfs_config_version: 0, diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 481004988d3ed..7074ef018a13c 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -169,7 +169,7 @@ pub(crate) fn handle_did_change_configuration( // Note that json can be null according to the spec if the client can't // provide a configuration. This is handled in Config::update below. let mut config = Config::clone(&*this.config); - config.update(json.take()); + this.config_errors = config.update(json.take()).err(); this.update_configuration(config); } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 49595a45ea9de..16d683957f041 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -602,21 +602,18 @@ impl GlobalState { (Progress::Begin, None) } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), - flycheck::Progress::DidCancel => (Progress::End, None), + flycheck::Progress::DidCancel => { + self.last_flycheck_error = None; + (Progress::End, None) + } flycheck::Progress::DidFailToRestart(err) => { - self.show_and_log_error( - "cargo check failed to start".to_string(), - Some(err), - ); + self.last_flycheck_error = + Some(format!("cargo check failed to start: {err}")); return; } flycheck::Progress::DidFinish(result) => { - if let Err(err) = result { - self.show_and_log_error( - "cargo check failed".to_string(), - Some(err.to_string()), - ); - } + self.last_flycheck_error = + result.err().map(|err| format!("cargo check failed to start: {err}")); (Progress::End, None) } }; diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 5300349e7d2ce..4e29485573822 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -139,6 +139,11 @@ impl GlobalState { status.health = lsp_ext::Health::Warning; format_to!(message, "{err}\n"); } + if let Some(err) = &self.last_flycheck_error { + status.health = lsp_ext::Health::Warning; + message.push_str(err); + message.push('\n'); + } for ws in self.workspaces.iter() { let (ProjectWorkspace::Cargo { sysroot, .. } From 0b507c6f0434ced76da4bf068551b93b6df47a5f Mon Sep 17 00:00:00 2001 From: disco07 Date: Fri, 26 May 2023 06:16:39 +0200 Subject: [PATCH 514/806] redundant pattern matches! result --- clippy_lints/src/matches/mod.rs | 8 +++- .../src/matches/redundant_pattern_match.rs | 21 ++++----- .../redundant_pattern_matching_option.fixed | 12 +++++ tests/ui/redundant_pattern_matching_option.rs | 12 +++++ .../redundant_pattern_matching_option.stderr | 44 +++++++++++------- .../redundant_pattern_matching_result.fixed | 15 ++++++ tests/ui/redundant_pattern_matching_result.rs | 15 ++++++ .../redundant_pattern_matching_result.stderr | 46 ++++++++++++------- 8 files changed, 125 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 55ec9d4474f59..0d91051632a18 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -25,7 +25,7 @@ mod wild_in_or_pats; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text}; +use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -974,12 +974,16 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) { return; } let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { + if is_direct_expn_of(expr.span, "matches").is_some() { + redundant_pattern_match::check_match(cx, expr, ex, arms); + } + if source == MatchSource::Normal && !is_span_match(cx, expr.span) { return; } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index e81e09da42547..479cfd835126c 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,10 +1,10 @@ use super::REDUNDANT_PATTERN_MATCHING; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; -use clippy_utils::{higher, is_trait_method}; +use clippy_utils::{higher, is_expn_of, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -190,24 +190,19 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); if let Some(good_method) = found_good_method(cx, arms, node_pair) { - let span = expr.span.to(op.span); + let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - span_lint_and_then( + span_lint_and_sugg( cx, REDUNDANT_PATTERN_MATCHING, - expr.span, + span, &format!("redundant pattern matching, consider using `{good_method}`"), - |diag| { - diag.span_suggestion( - span, - "try this", - format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), - Applicability::MaybeIncorrect, // snippet - ); - }, + "try this", + format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), + Applicability::MachineApplicable, ); } } diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index accdf1da9ddc9..dae931541d454 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -47,6 +47,7 @@ fn main() { issue6067(); issue10726(); + issue10803(); let _ = if gen_opt().is_some() { 1 @@ -107,3 +108,14 @@ fn issue10726() { _ => false, }; } + +fn issue10803() { + let x = Some(42); + + let _ = x.is_some(); + + let _ = x.is_none(); + + // Don't lint + let _ = matches!(x, Some(16)); +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index ec684bdf71c11..3f2fa3f53ce99 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -56,6 +56,7 @@ fn main() { issue6067(); issue10726(); + issue10803(); let _ = if let Some(_) = gen_opt() { 1 @@ -134,3 +135,14 @@ fn issue10726() { _ => false, }; } + +fn issue10803() { + let x = Some(42); + + let _ = matches!(x, Some(_)); + + let _ = matches!(x, None); + + // Don't lint + let _ = matches!(x, Some(16)); +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index a69eb3905205f..93760ce97a8f6 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -77,49 +77,49 @@ LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:60:20 + --> $DIR/redundant_pattern_matching_option.rs:61:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:62:19 + --> $DIR/redundant_pattern_matching_option.rs:63:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:68:12 + --> $DIR/redundant_pattern_matching_option.rs:69:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:83:12 + --> $DIR/redundant_pattern_matching_option.rs:84:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:85:12 + --> $DIR/redundant_pattern_matching_option.rs:86:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:87:15 + --> $DIR/redundant_pattern_matching_option.rs:88:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:89:15 + --> $DIR/redundant_pattern_matching_option.rs:90:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:91:5 + --> $DIR/redundant_pattern_matching_option.rs:92:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:96:5 + --> $DIR/redundant_pattern_matching_option.rs:97:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,19 +137,19 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:104:12 + --> $DIR/redundant_pattern_matching_option.rs:105:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:105:12 + --> $DIR/redundant_pattern_matching_option.rs:106:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:111:5 + --> $DIR/redundant_pattern_matching_option.rs:112:5 | LL | / match x { LL | | Some(_) => true, @@ -158,7 +158,7 @@ LL | | }; | |_____^ help: try this: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:116:5 + --> $DIR/redundant_pattern_matching_option.rs:117:5 | LL | / match x { LL | | None => true, @@ -167,7 +167,7 @@ LL | | }; | |_____^ help: try this: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:121:5 + --> $DIR/redundant_pattern_matching_option.rs:122:5 | LL | / match x { LL | | Some(_) => false, @@ -176,7 +176,7 @@ LL | | }; | |_____^ help: try this: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:126:5 + --> $DIR/redundant_pattern_matching_option.rs:127:5 | LL | / match x { LL | | None => false, @@ -184,5 +184,17 @@ LL | | _ => true, LL | | }; | |_____^ help: try this: `x.is_some()` -error: aborting due to 26 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:142:13 + | +LL | let _ = matches!(x, Some(_)); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:144:13 + | +LL | let _ = matches!(x, None); + | ^^^^^^^^^^^^^^^^^ help: try this: `x.is_none()` + +error: aborting due to 28 previous errors diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index e4032ae44b715..d77a2af761642 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -44,6 +44,7 @@ fn main() { issue6067(); issue6065(); issue10726(); + issue10803(); let _ = if gen_res().is_ok() { 1 @@ -133,3 +134,17 @@ fn issue10726() { _ => true, }; } + +fn issue10803() { + let x: Result = Ok(42); + + let _ = x.is_ok(); + + let _ = x.is_err(); + + // Don't lint + let _ = matches!(x, Ok(16)); + + // Don't lint + let _ = matches!(x, Err(16)); +} diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 39eb10df8789e..aa884ac6bb1ee 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -56,6 +56,7 @@ fn main() { issue6067(); issue6065(); issue10726(); + issue10803(); let _ = if let Ok(_) = gen_res() { 1 @@ -163,3 +164,17 @@ fn issue10726() { _ => true, }; } + +fn issue10803() { + let x: Result = Ok(42); + + let _ = matches!(x, Ok(_)); + + let _ = matches!(x, Err(_)); + + // Don't lint + let _ = matches!(x, Ok(16)); + + // Don't lint + let _ = matches!(x, Err(16)); +} diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index 5893ae4dcc492..b462f7f41b9cc 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:61:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:63:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:86:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:87:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:93:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:94:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:114:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:118:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:120:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:125:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -151,7 +151,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:134:5 + --> $DIR/redundant_pattern_matching_result.rs:135:5 | LL | / match x { LL | | Ok(_) => true, @@ -160,7 +160,7 @@ LL | | }; | |_____^ help: try this: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:139:5 + --> $DIR/redundant_pattern_matching_result.rs:140:5 | LL | / match x { LL | | Ok(_) => false, @@ -169,7 +169,7 @@ LL | | }; | |_____^ help: try this: `x.is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:144:5 + --> $DIR/redundant_pattern_matching_result.rs:145:5 | LL | / match x { LL | | Err(_) => true, @@ -178,7 +178,7 @@ LL | | }; | |_____^ help: try this: `x.is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:149:5 + --> $DIR/redundant_pattern_matching_result.rs:150:5 | LL | / match x { LL | | Err(_) => false, @@ -186,5 +186,17 @@ LL | | _ => true, LL | | }; | |_____^ help: try this: `x.is_ok()` -error: aborting due to 26 previous errors +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:171:13 + | +LL | let _ = matches!(x, Ok(_)); + | ^^^^^^^^^^^^^^^^^^ help: try this: `x.is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:173:13 + | +LL | let _ = matches!(x, Err(_)); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_err()` + +error: aborting due to 28 previous errors From be9cc0baaea4285e332b32f771302b0852961a3f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 16:41:45 +0200 Subject: [PATCH 515/806] Render size, align and offset hover values in hex --- crates/hir-ty/src/layout.rs | 35 +++++---- crates/hir/src/lib.rs | 44 +++++++---- crates/ide/src/hover/render.rs | 75 +++++++++++------- crates/ide/src/hover/tests.rs | 103 +++++++++++++++---------- crates/ide/src/inlay_hints/chaining.rs | 12 +-- crates/test-utils/src/minicore.rs | 6 ++ 6 files changed, 165 insertions(+), 110 deletions(-) diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index c383e6d346692..693c0494dbd74 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -1,7 +1,7 @@ //! Compute the binary representation of a type use base_db::CrateId; -use chalk_ir::{AdtId, TyKind}; +use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size, @@ -83,7 +83,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result db.layout_of_adt(*def, subst.clone(), krate)?, TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( @@ -104,12 +104,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result dl.ptr_sized_integer(), - chalk_ir::IntTy::I8 => Integer::I8, - chalk_ir::IntTy::I16 => Integer::I16, - chalk_ir::IntTy::I32 => Integer::I32, - chalk_ir::IntTy::I64 => Integer::I64, - chalk_ir::IntTy::I128 => Integer::I128, + IntTy::Isize => dl.ptr_sized_integer(), + IntTy::I8 => Integer::I8, + IntTy::I16 => Integer::I16, + IntTy::I32 => Integer::I32, + IntTy::I64 => Integer::I64, + IntTy::I128 => Integer::I128, }, true, ), @@ -118,12 +118,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result dl.ptr_sized_integer(), - chalk_ir::UintTy::U8 => Integer::I8, - chalk_ir::UintTy::U16 => Integer::I16, - chalk_ir::UintTy::U32 => Integer::I32, - chalk_ir::UintTy::U64 => Integer::I64, - chalk_ir::UintTy::U128 => Integer::I128, + UintTy::Usize => dl.ptr_sized_integer(), + UintTy::U8 => Integer::I8, + UintTy::U16 => Integer::I16, + UintTy::U32 => Integer::I32, + UintTy::U64 => Integer::I64, + UintTy::U128 => Integer::I128, }, false, ), @@ -131,8 +131,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result scalar( dl, match f { - chalk_ir::FloatTy::F32 => Primitive::F32, - chalk_ir::FloatTy::F64 => Primitive::F64, + FloatTy::F32 => Primitive::F32, + FloatTy::F64 => Primitive::F64, }, ), }, @@ -283,7 +283,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result return Err(LayoutError::HasPlaceholder), - }) + }; + Ok(layout) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 64f97452769d2..b1583c9d00b5e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1125,6 +1125,25 @@ impl Enum { pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } + + pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { + let layout = Adt::from(self).layout(db)?; + let tag_size = + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.variants { + match tag_encoding { + TagEncoding::Direct => { + let target_data_layout = db + .target_data_layout(self.module(db).krate().id) + .ok_or(LayoutError::TargetLayoutNotAvailable)?; + tag.size(&*target_data_layout).bytes_usize() + } + TagEncoding::Niche { .. } => 0, + } + } else { + 0 + }; + Ok((layout, tag_size)) + } } impl HasVisibility for Enum { @@ -1185,23 +1204,16 @@ impl Variant { /// Return layout of the variant and tag size of the parent enum. pub fn layout(&self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { let parent_enum = self.parent_enum(db); - let parent_layout = Adt::from(parent_enum).layout(db)?; - if let layout::Variants::Multiple { variants, tag, tag_encoding, tag_field: _ } = - parent_layout.variants - { - let tag_size = match tag_encoding { - TagEncoding::Direct => { - let target_data_layout = db - .target_data_layout(parent_enum.module(db).krate().id) - .ok_or(LayoutError::TargetLayoutNotAvailable)?; - tag.size(&*target_data_layout).bytes_usize() + let (parent_layout, tag_size) = parent_enum.layout(db)?; + Ok(( + match parent_layout.variants { + layout::Variants::Multiple { variants, .. } => { + variants[RustcEnumVariantIdx(self.id)].clone() } - TagEncoding::Niche { .. } => 0, - }; - Ok((variants[RustcEnumVariantIdx(self.id)].clone(), tag_size)) - } else { - Ok((parent_layout, 0)) - } + _ => parent_layout, + }, + tag_size, + )) } } diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index d9d4a1a99212d..4cbe7cca5afe3 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -401,11 +401,11 @@ pub(super) fn definition( hir::VariantDef::Struct(s) => Adt::from(s) .layout(db) .ok() - .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())), + .map(|layout| format!(", offset = {:#X}", layout.fields.offset(id).bytes())), _ => None, }; Some(format!( - "size = {}, align = {}{}", + "size = {:#X}, align = {:#X}{}", layout.size.bytes(), layout.align.abi.bytes(), offset.as_deref().unwrap_or_default() @@ -415,28 +415,38 @@ pub(super) fn definition( Definition::Function(it) => label_and_docs(db, it), Definition::Adt(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.layout(db).ok()?; - Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) + Some(format!( + "size = {:#X}, align = {:#X}", + layout.size.bytes(), + layout.align.abi.bytes() + )) }), - Definition::Variant(it) => label_value_and_layout_info_and_docs(db, it, config, |&it| { - let layout = (|| { + Definition::Variant(it) => label_value_and_layout_info_and_docs( + db, + it, + config, + |&it| { + if !it.parent_enum(db).is_data_carrying(db) { + match it.eval(db) { + Ok(x) => { + Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }) + } + Err(_) => it.value(db).map(|x| format!("{x:?}")), + } + } else { + None + } + }, + |it| { let (layout, tag_size) = it.layout(db).ok()?; let size = layout.size.bytes_usize() - tag_size; if size == 0 { // There is no value in showing layout info for fieldless variants return None; } - Some(format!("size = {}", layout.size.bytes())) - })(); - let value = if !it.parent_enum(db).is_data_carrying(db) { - match it.eval(db) { - Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }), - Err(_) => it.value(db).map(|x| format!("{x:?}")), - } - } else { - None - }; - (value, layout) - }), + Some(format!("size = {:#X}", layout.size.bytes())) + }, + ), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); match body { @@ -463,7 +473,11 @@ pub(super) fn definition( Definition::TraitAlias(it) => label_and_docs(db, it), Definition::TypeAlias(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.ty(db).layout(db).ok()?; - Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) + Some(format!( + "size = {:#X}, align = {:#X}", + layout.size.bytes(), + layout.align.abi.bytes() + )) }), Definition::BuiltinType(it) => { return famous_defs @@ -634,41 +648,42 @@ fn label_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, - value_extractor: E, + layout_extractor: E, ) -> (String, Option) where D: HasAttrs + HirDisplay, E: Fn(&D) -> Option, V: Display, { - let label = match value_extractor(&def) { - Some(value) if config.memory_layout => format!("{} // {value}", def.display(db)), + let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { + Some(layout) => format!("{} // {layout}", def.display(db)), _ => def.display(db).to_string(), }; let docs = def.attrs(db).docs(); (label, docs) } -fn label_value_and_layout_info_and_docs( +fn label_value_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, value_extractor: E, + layout_extractor: E2, ) -> (String, Option) where D: HasAttrs + HirDisplay, - E: Fn(&D) -> (Option, Option), + E: Fn(&D) -> Option, + E2: Fn(&D) -> Option, V: Display, L: Display, { - let (value, layout) = value_extractor(&def); - let label = if let Some(value) = value { - format!("{} = {value}", def.display(db)) - } else { - def.display(db).to_string() + let value = value_extractor(&def); + let label = match value { + Some(value) => format!("{} = {value}", def.display(db)), + None => def.display(db).to_string(), }; - let label = match layout { - Some(layout) if config.memory_layout => format!("{} // {layout}", label), + let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { + Some(layout) => format!("{} // {layout}", label), _ => label, }; let docs = def.attrs(db).docs(); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index ca6c169348d76..4e171867fbff0 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -667,7 +667,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 1, align = 1, offset = 4 + field_a: u8 // size = 0x1, align = 0x1, offset = 0x4 ``` "#]], ); @@ -692,7 +692,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 4, align = 4, offset = 0 + field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 ``` "#]], ); @@ -714,7 +714,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 4, align = 4, offset = 0 + field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 ``` "#]], ); @@ -1521,16 +1521,16 @@ fn test_hover_function_pointer_show_identifiers() { check( r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8 - ``` - "#]], + ```rust + type foo = fn(a: i32, b: i32) -> i32 // size = 0x8, align = 0x8 + ``` + "#]], ); } @@ -1539,16 +1539,16 @@ fn test_hover_function_pointer_no_identifier() { check( r#"type foo$0 = fn(i32, _: i32) -> i32;"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type foo = fn(i32, i32) -> i32 // size = 8, align = 8 - ``` - "#]], + ```rust + type foo = fn(i32, i32) -> i32 // size = 0x8, align = 0x8 + ``` + "#]], ); } @@ -1674,7 +1674,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0, align = 1 + struct Bar // size = 0x0, align = 0x1 ``` --- @@ -1710,7 +1710,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0, align = 1 + struct Bar // size = 0x0, align = 0x1 ``` --- @@ -1739,7 +1739,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0, align = 1 + struct Bar // size = 0x0, align = 0x1 ``` --- @@ -1767,7 +1767,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0, align = 1 + pub struct Bar // size = 0x0, align = 0x1 ``` --- @@ -1794,7 +1794,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0, align = 1 + pub struct Bar // size = 0x0, align = 0x1 ``` --- @@ -1883,7 +1883,28 @@ fn test_hover_layout_of_variant() { ``` ```rust - Variant1(u8, u16) // size = 4 + Variant1(u8, u16) // size = 0x4 + ``` + "#]], + ); +} + +#[test] +fn test_hover_layout_of_enum() { + check( + r#"enum $0Foo { + Variant1(u8, u16), + Variant2(i32, u8, i64), + }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + enum Foo // size = 0x10, align = 0x8 ``` "#]], ); @@ -3192,7 +3213,7 @@ fn main() { ``` ```rust - f: i32 // size = 4, align = 4, offset = 0 + f: i32 // size = 0x4, align = 0x4, offset = 0x0 ``` "#]], ); @@ -3730,16 +3751,16 @@ struct Foo; type Fo$0o2 = Foo<2>; "#, expect![[r#" - *Foo2* + *Foo2* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type Foo2 = Foo<2> // size = 0, align = 1 - ``` - "#]], + ```rust + type Foo2 = Foo<2> // size = 0x0, align = 0x1 + ``` + "#]], ); } @@ -4648,7 +4669,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - pub struct TheItem // size = 0, align = 1 + pub struct TheItem // size = 0x0, align = 0x1 ``` --- @@ -4796,7 +4817,7 @@ mod string { ``` ```rust - struct String // size = 0, align = 1 + struct String // size = 0x0, align = 0x1 ``` --- @@ -5465,7 +5486,7 @@ foo_macro!( ``` ```rust - pub struct Foo // size = 0, align = 1 + pub struct Foo // size = 0x0, align = 0x1 ``` --- @@ -5490,7 +5511,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo // size = 4, align = 4 + pub struct Foo // size = 0x4, align = 0x4 ``` --- @@ -5589,7 +5610,7 @@ enum Enum { ``` ```rust - RecordV { field: u32 } // size = 4 + RecordV { field: u32 } // size = 0x4 ``` "#]], ); @@ -5611,7 +5632,7 @@ enum Enum { ``` ```rust - field: u32 // size = 4, align = 4 + field: u32 // size = 0x4, align = 0x4 ``` "#]], ); @@ -6113,7 +6134,7 @@ fn test() { ``` ```rust - f: u32 // size = 4, align = 4, offset = 0 + f: u32 // size = 0x4, align = 0x4, offset = 0x0 ``` "#]], ); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index db98bf2f9bcbe..9f49344924958 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 9164..9172, + range: 9165..9173, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 9196..9200, + range: 9197..9201, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 9164..9172, + range: 9165..9173, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 9196..9200, + range: 9197..9201, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 9164..9172, + range: 9165..9173, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 9196..9200, + range: 9197..9201, }, ), tooltip: "", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 6d6c9af7f0462..7f4838888bd79 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -38,6 +38,7 @@ //! option: panic //! ord: eq, option //! panic: fmt +//! phantom_data: //! pin: //! range: //! result: @@ -119,6 +120,11 @@ pub mod marker { #[lang = "tuple_trait"] pub trait Tuple {} // endregion:fn + + // region:phantom_data + #[lang = "phantom_data"] + pub struct PhantomData; + // endregion:phantom_data } // region:default From 7dfef85be64a574577a2b7fe4d169e42503f0297 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Fri, 26 May 2023 11:50:07 -0400 Subject: [PATCH 516/806] fix: add a toggle to disable the dependency explorer. --- editors/code/package.json | 7 ++++++- editors/code/src/config.ts | 4 ++++ editors/code/src/ctx.ts | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 96d63b42393aa..390508b883e4f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -465,6 +465,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.showDependenciesExplorer": { + "markdownDescription": "Whether to show the dependencies view.", + "default": true, + "type": "boolean" + }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", @@ -2013,7 +2018,7 @@ { "id": "rustDependencies", "name": "Rust Dependencies", - "when": "inRustProject" + "when": "inRustProject && config.rust-analyzer.showDependenciesExplorer" } ] }, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d6b8cc7a56a07..c6d2bcc2b2a97 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -284,6 +284,10 @@ export class Config { get useRustcErrorCode() { return this.get("diagnostics.useRustcErrorCode"); } + + get showDependenciesExplorer() { + return this.get("showDependenciesExplorer"); + } } // the optional `cb?` parameter is meant to be used to add additional diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 8bed74b88eaa6..a72b5391ff130 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -263,7 +263,10 @@ export class Ctx { } await client.start(); this.updateCommands(); - this.prepareTreeDependenciesView(client); + + if (this.config.showDependenciesExplorer) { + this.prepareTreeDependenciesView(client); + } } private prepareTreeDependenciesView(client: lc.LanguageClient) { From aa65395c496083d96206c6f4c331a56382b4c0da Mon Sep 17 00:00:00 2001 From: jyn Date: Fri, 26 May 2023 11:59:25 -0500 Subject: [PATCH 517/806] Update proc-macro-api for the new rustc metadata format --- crates/proc-macro-api/src/version.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/proc-macro-api/src/version.rs b/crates/proc-macro-api/src/version.rs index cf637ec359a2d..13f67a0128a7e 100644 --- a/crates/proc-macro-api/src/version.rs +++ b/crates/proc-macro-api/src/version.rs @@ -122,7 +122,7 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result { // https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632 let snappy_portion = match version { 5 | 6 => &dot_rustc[8..], - 7 => { + 7 | 8 => { let len_bytes = &dot_rustc[8..12]; let data_len = u32::from_be_bytes(len_bytes.try_into().unwrap()) as usize; &dot_rustc[12..data_len + 12] From 8458c6adb131345dbf0b48e23c8d220dc41c19fd Mon Sep 17 00:00:00 2001 From: jyn Date: Fri, 26 May 2023 12:08:58 -0500 Subject: [PATCH 518/806] Add other workspaces to `linkedProjects` in `rust_analyzer_settings.json` This makes go-to-definition, etc. work in cg_clif, cg_gcc, rust-analyzer, and src/tools/x. --- src/bootstrap/setup.rs | 1 + src/etc/rust_analyzer_settings.json | 9 ++++++++- src/tools/x/Cargo.lock | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 09f26862b4ab2..606a064bf6d20 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -31,6 +31,7 @@ static SETTINGS_HASHES: &[&str] = &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", + "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", ]; static RUST_ANALYZER_SETTINGS: &str = include_str!("../etc/rust_analyzer_settings.json"); diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index dd01bfaa7252d..d9c4645f0b3b7 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -7,7 +7,14 @@ "check", "--json-output" ], - "rust-analyzer.linkedProjects": ["src/bootstrap/Cargo.toml", "Cargo.toml"], + "rust-analyzer.linkedProjects": [ + "Cargo.toml", + "src/tools/x/Cargo.toml", + "src/bootstrap/Cargo.toml", + "src/tools/rust-analyzer/Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml" + ], "rust-analyzer.rustfmt.overrideCommand": [ "./build/host/rustfmt/bin/rustfmt", "--edition=2021" diff --git a/src/tools/x/Cargo.lock b/src/tools/x/Cargo.lock index 723d6cb25ed6a..09e5c7507490d 100644 --- a/src/tools/x/Cargo.lock +++ b/src/tools/x/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "x" -version = "0.1.0" +version = "0.1.1" From 28442403a42efbbaddf63ee1e83bd25060a55046 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 20:24:28 +0200 Subject: [PATCH 519/806] Run CI on nightly rust when proc-macro-* crates change --- .github/workflows/ci.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4124bfcbc62a8..ce6624b1994bc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,16 +32,21 @@ jobs: filters: | typescript: - 'editors/code/**' + proc_macros: + - 'crates/proc-macro-api/**' + - 'crates/proc-macro-srv/**' + - 'crates/proc-macro-srv-cli/**' + - 'crates/proc-macro-test/**' rust: + needs: changes if: github.repository == 'rust-lang/rust-analyzer' name: Rust runs-on: ${{ matrix.os }} env: CC: deny_c - # we want to build r-a on stable to check that it keeps building on stable, - # but we also want to test our proc-macro-srv which depends on nightly features - RUSTC_BOOTSTRAP: 1 + RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable'}}" + USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || ''}}" strategy: fail-fast: false @@ -57,7 +62,7 @@ jobs: - name: Install Rust toolchain run: | - rustup update --no-self-update stable + rustup update --no-self-update ${{ env.RUST_CHANNEL }} rustup component add rustfmt rust-src - name: Cache Dependencies @@ -68,15 +73,15 @@ jobs: run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml - name: Compile (tests) - run: cargo test --no-run --locked --features sysroot-abi + run: cargo test --no-run --locked ${{ env.USE_SYSROOT_ABI }} # It's faster to `test` before `build` ¯\_(ツ)_/¯ - name: Compile (rust-analyzer) if: matrix.os == 'ubuntu-latest' - run: cargo build --quiet --features sysroot-abi + run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }} - name: Test - run: cargo test --features sysroot-abi -- --nocapture --quiet + run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet - name: Run analysis-stats on rust-analyzer if: matrix.os == 'ubuntu-latest' From 934df697e3b6ab88246e4742a71dfecae833447a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 20:30:54 +0200 Subject: [PATCH 520/806] Add the toolchain channel as a caching key for CI --- .github/workflows/ci.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ce6624b1994bc..220d88afe4dcb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -66,7 +66,9 @@ jobs: rustup component add rustfmt rust-src - name: Cache Dependencies - uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e + uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + with: + key: ${{ env.RUST_CHANNEL }} - name: Bump opt-level if: matrix.os == 'ubuntu-latest' @@ -113,7 +115,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e + uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 - name: Check run: | From ee8c18cb6e757e24f968e51b26d39d4a442005aa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 20:54:58 +0200 Subject: [PATCH 521/806] Fix out_dirs_check test on stable --- crates/rust-analyzer/tests/slow-tests/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 54a91df23e5db..e130c762fc48a 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -752,6 +752,9 @@ fn main() { "enable": true }, "sysroot": null, + "extraEnv": { + "RUSTC_BOOTSTRAP": "1" + } } })) .server() From 35b208aaa72b5848050ac4e2d3094ecd948cf3a5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 21:52:39 +0200 Subject: [PATCH 522/806] Filter out unused cargo features from config --- .github/workflows/ci.yaml | 1 - Cargo.lock | 1 + crates/project-model/Cargo.toml | 1 + crates/project-model/src/build_scripts.rs | 28 +++++++++++++++---- crates/project-model/src/cargo_workspace.rs | 17 ++++++++++- crates/rust-analyzer/src/cargo_target_spec.rs | 14 +++++++--- 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 220d88afe4dcb..622da105fdd44 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -58,7 +58,6 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 20 - name: Install Rust toolchain run: | diff --git a/Cargo.lock b/Cargo.lock index e7ae42a2d9b8a..f9c5417ffb573 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1334,6 +1334,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", + "itertools", "la-arena", "paths", "profile", diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index efe61eb8f68db..3abff64a83b2f 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -21,6 +21,7 @@ serde.workspace = true triomphe.workspace = true anyhow = "1.0.62" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +itertools = "0.10.5" # local deps base-db.workspace = true diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index fe4cbfc88672d..6cbf403cb2e14 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -14,9 +14,10 @@ use std::{ }; use cargo_metadata::{camino::Utf8Path, Message}; +use itertools::Itertools; use la_arena::ArenaMap; use paths::{AbsPath, AbsPathBuf}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use serde::Deserialize; @@ -56,7 +57,10 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command(config: &CargoConfig) -> io::Result { + fn build_command( + config: &CargoConfig, + allowed_features: &FxHashSet, + ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -88,7 +92,12 @@ impl WorkspaceBuildScripts { } if !features.is_empty() { cmd.arg("--features"); - cmd.arg(features.join(" ")); + cmd.arg( + features + .iter() + .filter(|&feat| allowed_features.contains(feat)) + .join(","), + ); } } } @@ -127,13 +136,20 @@ impl WorkspaceBuildScripts { } .as_ref(); - match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) { + let allowed_features = workspace.workspace_features(); + + match Self::run_per_ws( + Self::build_command(config, &allowed_features)?, + workspace, + current_dir, + progress, + ) { Ok(WorkspaceBuildScripts { error: Some(error), .. }) if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => { // building build scripts failed, attempt to build with --keep-going so // that we potentially get more build data - let mut cmd = Self::build_command(config)?; + let mut cmd = Self::build_command(config, &allowed_features)?; cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; res.error = Some(error); @@ -161,7 +177,7 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config)?; + let cmd = Self::build_command(config, &Default::default())?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index e3fdeb448db5e..649a149504cfa 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -10,7 +10,7 @@ use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use serde_json::from_value; @@ -491,6 +491,21 @@ impl CargoWorkspace { None } + /// Returns the union of the features of all member crates in this workspace. + pub fn workspace_features(&self) -> FxHashSet { + self.packages() + .filter_map(|package| { + let package = &self[package]; + if package.is_member { + Some(package.features.keys().cloned()) + } else { + None + } + }) + .flatten() + .collect() + } + fn is_unique(&self, name: &str) -> bool { self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 } diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index cf51cf15a0e1d..3035dc333080e 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -5,6 +5,7 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; use ide::{Cancellable, FileId, RunnableKind, TestId}; use project_model::{self, CargoFeatures, ManifestPath, TargetKind}; +use rustc_hash::FxHashSet; use vfs::AbsPathBuf; use crate::global_state::GlobalStateSnapshot; @@ -21,6 +22,7 @@ pub(crate) struct CargoTargetSpec { pub(crate) target: String, pub(crate) target_kind: TargetKind, pub(crate) required_features: Vec, + pub(crate) features: FxHashSet, } impl CargoTargetSpec { @@ -73,12 +75,13 @@ impl CargoTargetSpec { } } - let target_required_features = if let Some(mut spec) = spec { + let (allowed_features, target_required_features) = if let Some(mut spec) = spec { + let allowed_features = mem::take(&mut spec.features); let required_features = mem::take(&mut spec.required_features); spec.push_to(&mut args, kind); - required_features + (allowed_features, required_features) } else { - Vec::new() + (Default::default(), Default::default()) }; let cargo_config = snap.config.cargo(); @@ -97,7 +100,9 @@ impl CargoTargetSpec { required_features(cfg, &mut feats); } - feats.extend(features.iter().cloned()); + feats.extend( + features.iter().filter(|&feat| allowed_features.contains(feat)).cloned(), + ); feats.extend(target_required_features); feats.dedup(); @@ -136,6 +141,7 @@ impl CargoTargetSpec { target: target_data.name.clone(), target_kind: target_data.kind, required_features: target_data.required_features.clone(), + features: package_data.features.keys().cloned().collect(), }; Ok(Some(res)) From def3b5d7dd906fd83b24648919f2e86a10afe6a3 Mon Sep 17 00:00:00 2001 From: Mason Shuler Date: Fri, 26 May 2023 16:40:30 -0400 Subject: [PATCH 523/806] Remove outdated rustup installation issue description --- docs/user/manual.adoc | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 419f7bac81034..babf305794d63 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -181,19 +181,6 @@ On Unix, running the editor from a shell or changing the `.desktop` file to set $ rustup component add rust-analyzer ---- -However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using: -[source,bash] ----- -$ rustup which --toolchain stable rust-analyzer ----- -You can link to there from `~/.cargo/bin` or configure your editor to use the full path. - -Alternatively you might be able to configure your editor to start `rust-analyzer` using the command: -[source,bash] ----- -$ rustup run stable rust-analyzer ----- - ==== Arch Linux The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): From 05d63eff9caa862f3a66e652cd0b247148c316ab Mon Sep 17 00:00:00 2001 From: Mason Shuler Date: Fri, 26 May 2023 16:41:51 -0400 Subject: [PATCH 524/806] Make rustup heading title match other titles --- docs/user/manual.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index babf305794d63..5b9db10b093d7 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -172,7 +172,7 @@ $ cargo xtask install --server If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. -==== `rustup` +==== rustup `rust-analyzer` is available in `rustup`: From cd4bffdd69c33e3987307ef06db3096385fbeec9 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 27 May 2023 00:28:11 +0330 Subject: [PATCH 525/806] Evaluate `UnevalutedConst` before trait solving --- crates/hir-ty/src/infer/unify.rs | 14 ++++----- crates/hir-ty/src/tests/regression.rs | 23 ++++++++++++++ crates/hir-ty/src/traits.rs | 14 ++++++--- crates/hir-ty/src/utils.rs | 43 +++++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 38eae475b556f..e33d8f1795e2e 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -15,11 +15,11 @@ use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys_and_consts, static_lifetime, to_chalk_trait_id, traits::FnTrait, - AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, - GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, - ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, - TyExt, TyKind, VariableKind, + consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, + to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, + DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, + Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -256,10 +256,10 @@ impl<'a> InferenceTable<'a> { { eval } else { - c + unknown_const(c.data(Interner).ty.clone()) } } else { - c + unknown_const(c.data(Interner).ty.clone()) } } _ => c, diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 1fdeddede096c..8f4b807f560b4 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1912,3 +1912,26 @@ fn main() { "#, ); } + +#[test] +fn regression_14844_2() { + check_no_mismatches( + r#" +//- minicore: fn +pub const ONE: usize = 1; + +pub type MyInner = Inner; + +pub struct Inner(); + +impl Inner<1> { + fn map(&self, func: F) -> bool + where + F: Fn(&MyInner) -> bool, + { + func(self) + } +} + "#, + ); +} diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index d2ca6e5ed7c8f..f40b7db3a5512 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -2,7 +2,7 @@ use std::env::var; -use chalk_ir::GoalData; +use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData}; use chalk_recursive::Cache; use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver}; @@ -16,9 +16,9 @@ use stdx::panic_context; use triomphe::Arc; use crate::{ - db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal, - Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty, - TyKind, WhereClause, + db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq, + AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause, }; /// This controls how much 'time' we give the Chalk solver before giving up. @@ -106,6 +106,12 @@ pub(crate) fn trait_solve_query( } } + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index aa40d084795a0..f60b4607f224c 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -4,7 +4,11 @@ use std::iter; use base_db::CrateId; -use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex, Mutability}; +use chalk_ir::{ + cast::Cast, + fold::{FallibleTypeFolder, Shift}, + BoundVar, DebruijnIndex, Mutability, +}; use either::Either; use hir_def::{ db::DefDatabase, @@ -26,8 +30,8 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use crate::{ - db::HirDatabase, ChalkTraitId, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, Ty, - TyExt, WhereClause, + consteval::unknown_const, db::HirDatabase, ChalkTraitId, Const, ConstScalar, GenericArg, + Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, }; pub(crate) fn fn_traits( @@ -403,3 +407,36 @@ pub(crate) fn pattern_matching_dereference_count( } r } + +pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> { + pub(crate) db: &'a dyn HirDatabase, +} + +impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { + type Error = (); + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_const( + &mut self, + constant: Const, + _outer_binder: DebruijnIndex, + ) -> Result { + if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value { + if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned { + if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + return Ok(eval); + } else { + return Ok(unknown_const(constant.data(Interner).ty.clone())); + } + } + } + Ok(constant) + } +} From 5a2094319b56b7caeb39ce08626ab9e4b79681dc Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 13 May 2023 18:11:27 +0200 Subject: [PATCH 526/806] Drop uplifted clippy::invalid_utf8_in_unchecked --- clippy_lints/src/declared_lints.rs | 1 - clippy_lints/src/invalid_utf8_in_unchecked.rs | 74 ------------- clippy_lints/src/lib.rs | 2 - clippy_lints/src/renamed_lints.rs | 1 + tests/ui/invalid_utf8_in_unchecked.rs | 20 ---- tests/ui/invalid_utf8_in_unchecked.stderr | 22 ---- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 104 +++++++++--------- 9 files changed, 60 insertions(+), 168 deletions(-) delete mode 100644 clippy_lints/src/invalid_utf8_in_unchecked.rs delete mode 100644 tests/ui/invalid_utf8_in_unchecked.rs delete mode 100644 tests/ui/invalid_utf8_in_unchecked.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 423eee47742e0..0ae95b045e03c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -212,7 +212,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, - crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, diff --git a/clippy_lints/src/invalid_utf8_in_unchecked.rs b/clippy_lints/src/invalid_utf8_in_unchecked.rs deleted file mode 100644 index 6a4861747d267..0000000000000 --- a/clippy_lints/src/invalid_utf8_in_unchecked.rs +++ /dev/null @@ -1,74 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_function_call, paths}; -use rustc_ast::{BorrowKind, LitKind}; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; -use rustc_span::Span; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal - /// - /// ### Why is this bad? - /// Creating such a `str` would result in undefined behavior - /// - /// ### Example - /// ```rust - /// # #[allow(unused)] - /// unsafe { - /// std::str::from_utf8_unchecked(b"cl\x82ippy"); - /// } - /// ``` - #[clippy::version = "1.64.0"] - pub INVALID_UTF8_IN_UNCHECKED, - correctness, - "using a non UTF-8 literal in `std::std::from_utf8_unchecked`" -} -declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]); - -impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) { - match &arg.kind { - ExprKind::Lit(Spanned { node: lit, .. }) => { - if let LitKind::ByteStr(bytes, _) = &lit - && std::str::from_utf8(bytes).is_err() - { - lint(cx, expr.span); - } - }, - ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => { - let elements = args.iter().map(|e|{ - match &e.kind { - ExprKind::Lit(Spanned { node: lit, .. }) => match lit { - LitKind::Byte(b) => Some(*b), - #[allow(clippy::cast_possible_truncation)] - LitKind::Int(b, _) => Some(*b as u8), - _ => None - } - _ => None - } - }).collect::>>(); - - if let Some(elements) = elements - && std::str::from_utf8(&elements).is_err() - { - lint(cx, expr.span); - } - } - _ => {} - } - } - } -} - -fn lint(cx: &LateContext<'_>, span: Span) { - span_lint( - cx, - INVALID_UTF8_IN_UNCHECKED, - span, - "non UTF-8 literal in `std::str::from_utf8_unchecked`", - ); -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b442a4ac5f611..fcca595c2bc4c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -157,7 +157,6 @@ mod inline_fn_without_body; mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; -mod invalid_utf8_in_unchecked; mod items_after_statements; mod items_after_test_module; mod iter_not_returning_iterator; @@ -937,7 +936,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); - store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index b0db56bb417ea..7c2a100efdac6 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -43,6 +43,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), ("clippy::let_underscore_drop", "let_underscore_drop"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::panic_params", "non_fmt_panics"), diff --git a/tests/ui/invalid_utf8_in_unchecked.rs b/tests/ui/invalid_utf8_in_unchecked.rs deleted file mode 100644 index 3dc096d3197fb..0000000000000 --- a/tests/ui/invalid_utf8_in_unchecked.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::invalid_utf8_in_unchecked)] - -fn main() { - // Valid - unsafe { - std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]); - std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']); - std::str::from_utf8_unchecked(b"clippy"); - - let x = 0xA0; - std::str::from_utf8_unchecked(&[0xC0, x]); - } - - // Invalid - unsafe { - std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]); - std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']); - std::str::from_utf8_unchecked(b"cl\x82ippy"); - } -} diff --git a/tests/ui/invalid_utf8_in_unchecked.stderr b/tests/ui/invalid_utf8_in_unchecked.stderr deleted file mode 100644 index c89cd2758ee9f..0000000000000 --- a/tests/ui/invalid_utf8_in_unchecked.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: non UTF-8 literal in `std::str::from_utf8_unchecked` - --> $DIR/invalid_utf8_in_unchecked.rs:16:9 - | -LL | std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings` - -error: non UTF-8 literal in `std::str::from_utf8_unchecked` - --> $DIR/invalid_utf8_in_unchecked.rs:17:9 - | -LL | std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: non UTF-8 literal in `std::str::from_utf8_unchecked` - --> $DIR/invalid_utf8_in_unchecked.rs:18:9 - | -LL | std::str::from_utf8_unchecked(b"cl/x82ippy"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index dfe45dec8a745..53ac65473b827 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -38,6 +38,7 @@ #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] @@ -87,6 +88,7 @@ #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] +#![warn(invalid_from_utf8_unchecked)] #![warn(let_underscore_drop)] #![warn(enum_intrinsics_non_enums)] #![warn(non_fmt_panics)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index ce8eca5a3081c..722c0b3eb2750 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -38,6 +38,7 @@ #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] @@ -87,6 +88,7 @@ #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] +#![warn(clippy::invalid_utf8_in_unchecked)] #![warn(clippy::let_underscore_drop)] #![warn(clippy::mem_discriminant_non_enum)] #![warn(clippy::panic_params)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 3fca60aa2ebd3..1ff8391766023 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,286 +7,292 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` + --> $DIR/rename.rs:91:9 + | +LL | #![warn(clippy::invalid_utf8_in_unchecked)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` + error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 48 previous errors +error: aborting due to 49 previous errors From c5e5d6958c62c8dd3623cacc03b43453e54e2724 Mon Sep 17 00:00:00 2001 From: sladynnunes Date: Sun, 28 May 2023 01:51:55 -0700 Subject: [PATCH 527/806] Migrate to Askama --- src/librustdoc/html/render/print_item.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 62027a3fa1941..761420c411f6a 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1446,11 +1446,11 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } -fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap(); } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. From 2924fd22138080ebf15b2aa05d31458d9fe3907d Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Thu, 25 May 2023 17:04:51 +1000 Subject: [PATCH 528/806] Implement custom QoS-aware thread pool This code replaces the thread pool implementation we were using previously (from the `threadpool` crate). By making the thread pool aware of QoS, each job spawned on the thread pool can have a different QoS class. This commit also replaces every QoS class used previously with Default as a temporary measure so that each usage can be chosen deliberately. --- Cargo.lock | 11 +-- crates/flycheck/src/lib.rs | 4 +- crates/ide/src/prime_caches.rs | 2 +- crates/rust-analyzer/Cargo.toml | 1 - crates/rust-analyzer/src/bin/main.rs | 2 +- crates/rust-analyzer/src/dispatch.rs | 55 ++++++++--- .../src/handlers/notification.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 34 ++++--- crates/rust-analyzer/src/reload.rs | 6 +- crates/rust-analyzer/src/task_pool.rs | 56 +++-------- crates/stdx/Cargo.toml | 1 + crates/stdx/src/thread.rs | 6 ++ crates/stdx/src/thread/pool.rs | 95 +++++++++++++++++++ crates/vfs-notify/src/lib.rs | 2 +- 14 files changed, 184 insertions(+), 93 deletions(-) create mode 100644 crates/stdx/src/thread/pool.rs diff --git a/Cargo.lock b/Cargo.lock index f9c5417ffb573..322a67383b030 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1518,7 +1518,6 @@ dependencies = [ "syntax", "test-utils", "thiserror", - "threadpool", "tikv-jemallocator", "toolchain", "tracing", @@ -1712,6 +1711,7 @@ version = "0.0.0" dependencies = [ "always-assert", "backtrace", + "crossbeam-channel", "jod-thread", "libc", "miow", @@ -1823,15 +1823,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "tikv-jemalloc-ctl" version = "0.5.0" diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index e40257c58f8a5..190205a2cde83 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -90,7 +90,7 @@ impl FlycheckHandle { ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); @@ -409,7 +409,7 @@ impl CargoHandle { let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index f049a225f077e..8c8a93bcb8fee 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs @@ -81,7 +81,7 @@ pub(crate) fn parallel_prime_caches( let worker = prime_caches_worker.clone(); let db = db.snapshot(); - stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + stdx::thread::Builder::new(stdx::thread::QoSClass::Default) .allow_leak(true) .spawn(move || Cancelled::catch(|| worker(db))) .expect("failed to spawn thread"); diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 3f795340b2f6b..97bd920920601 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -31,7 +31,6 @@ oorandom = "11.1.3" rustc-hash = "1.1.0" serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true -threadpool = "1.8.1" rayon = "1.6.1" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 3224aeae5645b..eba19333116a6 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -85,7 +85,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { // will make actions like hitting enter in the editor slow. // rust-analyzer does not block the editor’s render loop, // so we don’t use User Interactive. - with_extra_thread("LspServer", stdx::thread::QoSClass::UserInitiated, run_server)?; + with_extra_thread("LspServer", stdx::thread::QoSClass::Default, run_server)?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 313bb2ec8dffa..c4731340ba2eb 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -4,6 +4,7 @@ use std::{fmt, panic, thread}; use ide::Cancelled; use lsp_server::ExtractError; use serde::{de::DeserializeOwned, Serialize}; +use stdx::thread::QoSClass; use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, @@ -102,7 +103,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn({ + self.global_state.task_pool.handle.spawn(QoSClass::Default, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -128,6 +129,44 @@ impl<'a> RequestDispatcher<'a> { &mut self, f: fn(GlobalStateSnapshot, R::Params) -> Result, ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + R::Result: Serialize, + { + self.on_with_qos::(QoSClass::Default, f) + } + + /// Dispatches a latency-sensitive request onto the thread pool. + pub(crate) fn on_latency_sensitive( + &mut self, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + R::Result: Serialize, + { + self.on_with_qos::(QoSClass::Default, f) + } + + pub(crate) fn finish(&mut self) { + if let Some(req) = self.req.take() { + tracing::error!("unknown request: {:?}", req); + let response = lsp_server::Response::new_err( + req.id, + lsp_server::ErrorCode::MethodNotFound as i32, + "unknown request".to_string(), + ); + self.global_state.respond(response); + } + } + + fn on_with_qos( + &mut self, + qos_class: QoSClass, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> &mut Self where R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, @@ -138,7 +177,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn({ + self.global_state.task_pool.handle.spawn(qos_class, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -155,18 +194,6 @@ impl<'a> RequestDispatcher<'a> { self } - pub(crate) fn finish(&mut self) { - if let Some(req) = self.req.take() { - tracing::error!("unknown request: {:?}", req); - let response = lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::MethodNotFound as i32, - "unknown request".to_string(), - ); - self.global_state.respond(response); - } - } - fn parse(&mut self) -> Option<(lsp_server::Request, R::Params, String)> where R: lsp_types::request::Request, diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 7074ef018a13c..2d871748c3c88 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -291,7 +291,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } Ok(()) }; - state.task_pool.handle.spawn_with_sender(move |_| { + state.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |_| { if let Err(e) = std::panic::catch_unwind(task) { tracing::error!("flycheck task panicked: {e:?}") } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index a28edde2f4907..ae9f6ff7ee24b 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -397,7 +397,7 @@ impl GlobalState { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); - self.task_pool.handle.spawn_with_sender({ + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, { let analysis = self.snapshot().analysis; move |sender| { sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); @@ -678,7 +678,24 @@ impl GlobalState { .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) .on_sync::(handlers::handle_on_type_formatting) - // All other request handlers: + // We can’t run latency-sensitive request handlers which do semantic + // analysis on the main thread because that would block other + // requests. Instead, we run these request handlers on higher QoS + // threads in the threadpool. + .on_latency_sensitive::(handlers::handle_completion) + .on_latency_sensitive::( + handlers::handle_completion_resolve, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_full, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_full_delta, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_range, + ) + // All other request handlers .on::(handlers::fetch_dependency_list) .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) @@ -706,8 +723,6 @@ impl GlobalState { .on::(handlers::handle_goto_type_definition) .on_no_retry::(handlers::handle_inlay_hints) .on::(handlers::handle_inlay_hints_resolve) - .on::(handlers::handle_completion) - .on::(handlers::handle_completion_resolve) .on::(handlers::handle_code_lens) .on::(handlers::handle_code_lens_resolve) .on::(handlers::handle_folding_range) @@ -725,15 +740,6 @@ impl GlobalState { .on::( handlers::handle_call_hierarchy_outgoing, ) - .on::( - handlers::handle_semantic_tokens_full, - ) - .on::( - handlers::handle_semantic_tokens_full_delta, - ) - .on::( - handlers::handle_semantic_tokens_range, - ) .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) .finish(); @@ -781,7 +787,7 @@ impl GlobalState { tracing::trace!("updating notifications for {:?}", subscriptions); let snapshot = self.snapshot(); - self.task_pool.handle.spawn(move || { + self.task_pool.handle.spawn(stdx::thread::QoSClass::Default, move || { let _p = profile::span("publish_diagnostics"); let diagnostics = subscriptions .into_iter() diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 4e29485573822..7070950638d9c 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -185,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender({ + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -260,7 +260,7 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender(move |sender| { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); let progress = { @@ -280,7 +280,7 @@ impl GlobalState { let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); - self.task_pool.handle.spawn_with_sender(move |sender| { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |sender| { sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); let dummy_replacements = &dummy_replacements; diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index 0c5a4f3055347..f055de40d067c 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -1,76 +1,42 @@ -//! A thin wrapper around `ThreadPool` to make sure that we join all things -//! properly. -use std::sync::{Arc, Barrier}; +//! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs. +//! It is used in [`crate::global_state::GlobalState`] throughout the main loop. use crossbeam_channel::Sender; +use stdx::thread::{Pool, QoSClass}; pub(crate) struct TaskPool { sender: Sender, - inner: threadpool::ThreadPool, + pool: Pool, } impl TaskPool { pub(crate) fn new_with_threads(sender: Sender, threads: usize) -> TaskPool { - const STACK_SIZE: usize = 8 * 1024 * 1024; - - let inner = threadpool::Builder::new() - .thread_name("Worker".into()) - .thread_stack_size(STACK_SIZE) - .num_threads(threads) - .build(); - - // Set QoS of all threads in threadpool. - let barrier = Arc::new(Barrier::new(threads + 1)); - for _ in 0..threads { - let barrier = barrier.clone(); - inner.execute(move || { - stdx::thread::set_current_thread_qos_class(stdx::thread::QoSClass::Utility); - barrier.wait(); - }); - } - barrier.wait(); - - TaskPool { sender, inner } + TaskPool { sender, pool: Pool::new(threads) } } - pub(crate) fn spawn(&mut self, task: F) + pub(crate) fn spawn(&mut self, qos_class: QoSClass, task: F) where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - self.inner.execute({ + self.pool.spawn(qos_class, { let sender = self.sender.clone(); - move || { - if stdx::thread::IS_QOS_AVAILABLE { - debug_assert_eq!( - stdx::thread::get_current_thread_qos_class(), - Some(stdx::thread::QoSClass::Utility) - ); - } - - sender.send(task()).unwrap() - } + move || sender.send(task()).unwrap() }) } - pub(crate) fn spawn_with_sender(&mut self, task: F) + pub(crate) fn spawn_with_sender(&mut self, qos_class: QoSClass, task: F) where F: FnOnce(Sender) + Send + 'static, T: Send + 'static, { - self.inner.execute({ + self.pool.spawn(qos_class, { let sender = self.sender.clone(); move || task(sender) }) } pub(crate) fn len(&self) -> usize { - self.inner.queued_count() - } -} - -impl Drop for TaskPool { - fn drop(&mut self) { - self.inner.join() + self.pool.len() } } diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 986e3fcdcfc39..a67f36ae9006a 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -16,6 +16,7 @@ libc = "0.2.135" backtrace = { version = "0.3.65", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" +crossbeam-channel = "0.5.5" # Think twice before adding anything here [target.'cfg(windows)'.dependencies] diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 5042f001435e8..8630961f85bc7 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -13,6 +13,9 @@ use std::fmt; +mod pool; +pub use pool::Pool; + pub fn spawn(qos_class: QoSClass, f: F) -> JoinHandle where F: FnOnce() -> T, @@ -152,6 +155,8 @@ pub enum QoSClass { /// performance, responsiveness and efficiency. Utility, + Default, + /// TLDR: tasks that block using your app /// /// Contract: @@ -229,6 +234,7 @@ mod imp { let c = match class { QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, + QoSClass::Default => libc::qos_class_t::QOS_CLASS_DEFAULT, QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, }; diff --git a/crates/stdx/src/thread/pool.rs b/crates/stdx/src/thread/pool.rs new file mode 100644 index 0000000000000..b4ab9cb292ce4 --- /dev/null +++ b/crates/stdx/src/thread/pool.rs @@ -0,0 +1,95 @@ +//! [`Pool`] implements a basic custom thread pool +//! inspired by the [`threadpool` crate](http://docs.rs/threadpool). +//! It allows the spawning of tasks under different QoS classes. +//! rust-analyzer uses this to prioritize work based on latency requirements. +//! +//! The thread pool is implemented entirely using +//! the threading utilities in [`crate::thread`]. + +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; + +use crossbeam_channel::{Receiver, Sender}; + +use super::{ + get_current_thread_qos_class, set_current_thread_qos_class, Builder, JoinHandle, QoSClass, + IS_QOS_AVAILABLE, +}; + +pub struct Pool { + // `_handles` is never read: the field is present + // only for its `Drop` impl. + + // The worker threads exit once the channel closes; + // make sure to keep `job_sender` above `handles` + // so that the channel is actually closed + // before we join the worker threads! + job_sender: Sender, + _handles: Vec, + extant_tasks: Arc, +} + +struct Job { + requested_qos_class: QoSClass, + f: Box, +} + +impl Pool { + pub fn new(threads: usize) -> Pool { + const STACK_SIZE: usize = 8 * 1024 * 1024; + const INITIAL_QOS_CLASS: QoSClass = QoSClass::Utility; + + let (job_sender, job_receiver) = crossbeam_channel::unbounded(); + let extant_tasks = Arc::new(AtomicUsize::new(0)); + + let mut handles = Vec::with_capacity(threads); + for _ in 0..threads { + let handle = Builder::new(INITIAL_QOS_CLASS) + .stack_size(STACK_SIZE) + .name("Worker".into()) + .spawn({ + let extant_tasks = Arc::clone(&extant_tasks); + let job_receiver: Receiver = job_receiver.clone(); + move || { + let mut current_qos_class = INITIAL_QOS_CLASS; + for job in job_receiver { + if job.requested_qos_class != current_qos_class { + set_current_thread_qos_class(job.requested_qos_class); + current_qos_class = job.requested_qos_class; + } + extant_tasks.fetch_add(1, Ordering::SeqCst); + (job.f)(); + extant_tasks.fetch_sub(1, Ordering::SeqCst); + } + } + }) + .expect("failed to spawn thread"); + + handles.push(handle); + } + + Pool { _handles: handles, extant_tasks, job_sender } + } + + pub fn spawn(&self, qos_class: QoSClass, f: F) + where + F: FnOnce() + Send + 'static, + { + let f = Box::new(move || { + if IS_QOS_AVAILABLE { + debug_assert_eq!(get_current_thread_qos_class(), Some(qos_class)); + } + + f() + }); + + let job = Job { requested_qos_class: qos_class, f }; + self.job_sender.send(job).unwrap(); + } + + pub fn len(&self) -> usize { + self.extant_tasks.load(Ordering::SeqCst) + } +} diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 26f7a9fc42359..90a7d7d6c0e51 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle { fn spawn(sender: loader::Sender) -> NotifyHandle { let actor = NotifyActor::new(sender); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) .name("VfsLoader".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); From d0b001eed242ff8e2278f033db38f5ae0b3040e9 Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Thu, 25 May 2023 20:55:42 +1000 Subject: [PATCH 529/806] Use appropriate QoS classes throughout the codebase --- crates/flycheck/src/lib.rs | 4 ++-- crates/ide/src/prime_caches.rs | 2 +- crates/rust-analyzer/src/bin/main.rs | 2 +- crates/rust-analyzer/src/dispatch.rs | 11 ++++++----- crates/rust-analyzer/src/handlers/notification.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 7 +++++-- crates/rust-analyzer/src/reload.rs | 6 +++--- crates/stdx/src/thread.rs | 3 --- crates/vfs-notify/src/lib.rs | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 190205a2cde83..e40257c58f8a5 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -90,7 +90,7 @@ impl FlycheckHandle { ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); @@ -409,7 +409,7 @@ impl CargoHandle { let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index 8c8a93bcb8fee..f049a225f077e 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs @@ -81,7 +81,7 @@ pub(crate) fn parallel_prime_caches( let worker = prime_caches_worker.clone(); let db = db.snapshot(); - stdx::thread::Builder::new(stdx::thread::QoSClass::Default) + stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .allow_leak(true) .spawn(move || Cancelled::catch(|| worker(db))) .expect("failed to spawn thread"); diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index eba19333116a6..3224aeae5645b 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -85,7 +85,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { // will make actions like hitting enter in the editor slow. // rust-analyzer does not block the editor’s render loop, // so we don’t use User Interactive. - with_extra_thread("LspServer", stdx::thread::QoSClass::Default, run_server)?; + with_extra_thread("LspServer", stdx::thread::QoSClass::UserInitiated, run_server)?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index c4731340ba2eb..ca8c06c7530b7 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -88,7 +88,8 @@ impl<'a> RequestDispatcher<'a> { self } - /// Dispatches the request onto thread pool + /// Dispatches a non-latency-sensitive request onto the thread pool + /// without retrying it if it panics. pub(crate) fn on_no_retry( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> Result, @@ -103,7 +104,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(QoSClass::Default, { + self.global_state.task_pool.handle.spawn(QoSClass::Utility, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -124,7 +125,7 @@ impl<'a> RequestDispatcher<'a> { self } - /// Dispatches the request onto thread pool + /// Dispatches a non-latency-sensitive request onto the thread pool. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> Result, @@ -134,7 +135,7 @@ impl<'a> RequestDispatcher<'a> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_qos::(QoSClass::Default, f) + self.on_with_qos::(QoSClass::Utility, f) } /// Dispatches a latency-sensitive request onto the thread pool. @@ -147,7 +148,7 @@ impl<'a> RequestDispatcher<'a> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_qos::(QoSClass::Default, f) + self.on_with_qos::(QoSClass::UserInitiated, f) } pub(crate) fn finish(&mut self) { diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 2d871748c3c88..5270d858d573f 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -291,7 +291,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } Ok(()) }; - state.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |_| { + state.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |_| { if let Err(e) = std::panic::catch_unwind(task) { tracing::error!("flycheck task panicked: {e:?}") } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ae9f6ff7ee24b..d932fcf992fc7 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -397,7 +397,7 @@ impl GlobalState { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, { let analysis = self.snapshot().analysis; move |sender| { sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); @@ -787,7 +787,10 @@ impl GlobalState { tracing::trace!("updating notifications for {:?}", subscriptions); let snapshot = self.snapshot(); - self.task_pool.handle.spawn(stdx::thread::QoSClass::Default, move || { + + // Diagnostics are triggered by the user typing + // so we want computing them to run at the User Initiated QoS. + self.task_pool.handle.spawn(stdx::thread::QoSClass::UserInitiated, move || { let _p = profile::span("publish_diagnostics"); let diagnostics = subscriptions .into_iter() diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 7070950638d9c..29cfb3d46a5ab 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -185,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -260,7 +260,7 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |sender| { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); let progress = { @@ -280,7 +280,7 @@ impl GlobalState { let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Default, move |sender| { + self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |sender| { sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); let dummy_replacements = &dummy_replacements; diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 8630961f85bc7..478e23ca7d37e 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -155,8 +155,6 @@ pub enum QoSClass { /// performance, responsiveness and efficiency. Utility, - Default, - /// TLDR: tasks that block using your app /// /// Contract: @@ -234,7 +232,6 @@ mod imp { let c = match class { QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, - QoSClass::Default => libc::qos_class_t::QOS_CLASS_DEFAULT, QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, }; diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 90a7d7d6c0e51..26f7a9fc42359 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle { fn spawn(sender: loader::Sender) -> NotifyHandle { let actor = NotifyActor::new(sender); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Default) + let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) .name("VfsLoader".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); From 74bc2a47e09cbfa1f52479c083c0a0d4d502dbce Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sat, 27 May 2023 03:18:17 +1000 Subject: [PATCH 530/806] =?UTF-8?q?Wrap=20platform-specific=20QoS=20in=20r?= =?UTF-8?q?-a-specific=20=E2=80=9Cthread=20intent=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/flycheck/src/lib.rs | 4 +- crates/ide/src/prime_caches.rs | 2 +- crates/rust-analyzer/src/bin/main.rs | 18 +- crates/rust-analyzer/src/dispatch.rs | 14 +- .../src/handlers/notification.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 8 +- crates/rust-analyzer/src/reload.rs | 8 +- crates/rust-analyzer/src/task_pool.rs | 10 +- .../rust-analyzer/tests/slow-tests/support.rs | 2 +- crates/stdx/src/thread.rs | 261 ++-------------- crates/stdx/src/thread/intent.rs | 287 ++++++++++++++++++ crates/stdx/src/thread/pool.rs | 31 +- crates/vfs-notify/src/lib.rs | 2 +- 13 files changed, 354 insertions(+), 295 deletions(-) create mode 100644 crates/stdx/src/thread/intent.rs diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index e40257c58f8a5..fbb943ccb99dd 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -90,7 +90,7 @@ impl FlycheckHandle { ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); @@ -409,7 +409,7 @@ impl CargoHandle { let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index f049a225f077e..d704d12a05b23 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs @@ -81,7 +81,7 @@ pub(crate) fn parallel_prime_caches( let worker = prime_caches_worker.clone(); let db = db.snapshot(); - stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .allow_leak(true) .spawn(move || Cancelled::catch(|| worker(db))) .expect("failed to spawn thread"); diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 3224aeae5645b..91911dd180962 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -79,13 +79,15 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { return Ok(()); } - // rust-analyzer’s “main thread” is actually a secondary thread - // with an increased stack size at the User Initiated QoS class. - // We use this QoS class because any delay in the main loop + // rust-analyzer’s “main thread” is actually + // a secondary latency-sensitive thread with an increased stack size. + // We use this thread intent because any delay in the main loop // will make actions like hitting enter in the editor slow. - // rust-analyzer does not block the editor’s render loop, - // so we don’t use User Interactive. - with_extra_thread("LspServer", stdx::thread::QoSClass::UserInitiated, run_server)?; + with_extra_thread( + "LspServer", + stdx::thread::ThreadIntent::LatencySensitive, + run_server, + )?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, @@ -143,10 +145,10 @@ const STACK_SIZE: usize = 1024 * 1024 * 8; /// space. fn with_extra_thread( thread_name: impl Into, - qos_class: stdx::thread::QoSClass, + thread_intent: stdx::thread::ThreadIntent, f: impl FnOnce() -> Result<()> + Send + 'static, ) -> Result<()> { - let handle = stdx::thread::Builder::new(qos_class) + let handle = stdx::thread::Builder::new(thread_intent) .name(thread_name.into()) .stack_size(STACK_SIZE) .spawn(f)?; diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index ca8c06c7530b7..ebe77b8dfe721 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -4,7 +4,7 @@ use std::{fmt, panic, thread}; use ide::Cancelled; use lsp_server::ExtractError; use serde::{de::DeserializeOwned, Serialize}; -use stdx::thread::QoSClass; +use stdx::thread::ThreadIntent; use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, @@ -104,7 +104,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(QoSClass::Utility, { + self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -135,7 +135,7 @@ impl<'a> RequestDispatcher<'a> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_qos::(QoSClass::Utility, f) + self.on_with_thread_intent::(ThreadIntent::Worker, f) } /// Dispatches a latency-sensitive request onto the thread pool. @@ -148,7 +148,7 @@ impl<'a> RequestDispatcher<'a> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_qos::(QoSClass::UserInitiated, f) + self.on_with_thread_intent::(ThreadIntent::LatencySensitive, f) } pub(crate) fn finish(&mut self) { @@ -163,9 +163,9 @@ impl<'a> RequestDispatcher<'a> { } } - fn on_with_qos( + fn on_with_thread_intent( &mut self, - qos_class: QoSClass, + intent: ThreadIntent, f: fn(GlobalStateSnapshot, R::Params) -> Result, ) -> &mut Self where @@ -178,7 +178,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(qos_class, { + self.global_state.task_pool.handle.spawn(intent, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 5270d858d573f..09de6900c8fb5 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -291,7 +291,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } Ok(()) }; - state.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |_| { + state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { if let Err(e) = std::panic::catch_unwind(task) { tracing::error!("flycheck task panicked: {e:?}") } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index d932fcf992fc7..34947fcd6f95b 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -397,7 +397,7 @@ impl GlobalState { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, { + self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, { let analysis = self.snapshot().analysis; move |sender| { sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); @@ -680,7 +680,7 @@ impl GlobalState { .on_sync::(handlers::handle_on_type_formatting) // We can’t run latency-sensitive request handlers which do semantic // analysis on the main thread because that would block other - // requests. Instead, we run these request handlers on higher QoS + // requests. Instead, we run these request handlers on higher priority // threads in the threadpool. .on_latency_sensitive::(handlers::handle_completion) .on_latency_sensitive::( @@ -789,8 +789,8 @@ impl GlobalState { let snapshot = self.snapshot(); // Diagnostics are triggered by the user typing - // so we want computing them to run at the User Initiated QoS. - self.task_pool.handle.spawn(stdx::thread::QoSClass::UserInitiated, move || { + // so we run them on a latency sensitive thread. + self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || { let _p = profile::span("publish_diagnostics"); let diagnostics = subscriptions .into_iter() diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 29cfb3d46a5ab..6e8c8ea91a1b3 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -27,7 +27,7 @@ use ide_db::{ use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; -use stdx::format_to; +use stdx::{format_to, thread::ThreadIntent}; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; @@ -185,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -260,7 +260,7 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |sender| { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); let progress = { @@ -280,7 +280,7 @@ impl GlobalState { let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); - self.task_pool.handle.spawn_with_sender(stdx::thread::QoSClass::Utility, move |sender| { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); let dummy_replacements = &dummy_replacements; diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index f055de40d067c..a5a10e86914e0 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -2,7 +2,7 @@ //! It is used in [`crate::global_state::GlobalState`] throughout the main loop. use crossbeam_channel::Sender; -use stdx::thread::{Pool, QoSClass}; +use stdx::thread::{Pool, ThreadIntent}; pub(crate) struct TaskPool { sender: Sender, @@ -14,23 +14,23 @@ impl TaskPool { TaskPool { sender, pool: Pool::new(threads) } } - pub(crate) fn spawn(&mut self, qos_class: QoSClass, task: F) + pub(crate) fn spawn(&mut self, intent: ThreadIntent, task: F) where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - self.pool.spawn(qos_class, { + self.pool.spawn(intent, { let sender = self.sender.clone(); move || sender.send(task()).unwrap() }) } - pub(crate) fn spawn_with_sender(&mut self, qos_class: QoSClass, task: F) + pub(crate) fn spawn_with_sender(&mut self, intent: ThreadIntent, task: F) where F: FnOnce(Sender) + Send + 'static, T: Send + 'static, { - self.pool.spawn(qos_class, { + self.pool.spawn(intent, { let sender = self.sender.clone(); move || task(sender) }) diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 33d7f6576c3cd..b2a8041ae9b57 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -165,7 +165,7 @@ impl Server { fn new(dir: TestDir, config: Config) -> Server { let (connection, client) = Connection::memory(); - let _thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("test server".to_string()) .spawn(move || main_loop(config, connection).unwrap()) .expect("failed to spawn a thread"); diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 478e23ca7d37e..e577eb4313714 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs @@ -1,39 +1,46 @@ //! A utility module for working with threads that automatically joins threads upon drop -//! and provides functionality for interfacing with operating system quality of service (QoS) APIs. +//! and abstracts over operating system quality of service (QoS) APIs +//! through the concept of a “thread intent”. +//! +//! The intent of a thread is frozen at thread creation time, +//! i.e. there is no API to change the intent of a thread once it has been spawned. //! //! As a system, rust-analyzer should have the property that //! old manual scheduling APIs are replaced entirely by QoS. //! To maintain this invariant, we panic when it is clear that //! old scheduling APIs have been used. //! -//! Moreover, we also want to ensure that every thread has a QoS set explicitly +//! Moreover, we also want to ensure that every thread has an intent set explicitly //! to force a decision about its importance to the system. -//! Thus, [`QoSClass`] has no default value -//! and every entry point to creating a thread requires a [`QoSClass`] upfront. +//! Thus, [`ThreadIntent`] has no default value +//! and every entry point to creating a thread requires a [`ThreadIntent`] upfront. use std::fmt; +mod intent; mod pool; + +pub use intent::ThreadIntent; pub use pool::Pool; -pub fn spawn(qos_class: QoSClass, f: F) -> JoinHandle +pub fn spawn(intent: ThreadIntent, f: F) -> JoinHandle where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static, { - Builder::new(qos_class).spawn(f).expect("failed to spawn thread") + Builder::new(intent).spawn(f).expect("failed to spawn thread") } pub struct Builder { - qos_class: QoSClass, + intent: ThreadIntent, inner: jod_thread::Builder, allow_leak: bool, } impl Builder { - pub fn new(qos_class: QoSClass) -> Builder { - Builder { qos_class, inner: jod_thread::Builder::new(), allow_leak: false } + pub fn new(intent: ThreadIntent) -> Builder { + Builder { intent, inner: jod_thread::Builder::new(), allow_leak: false } } pub fn name(self, name: String) -> Builder { @@ -55,7 +62,7 @@ impl Builder { T: Send + 'static, { let inner_handle = self.inner.spawn(move || { - set_current_thread_qos_class(self.qos_class); + self.intent.apply_to_current_thread(); f() })?; @@ -93,237 +100,3 @@ impl fmt::Debug for JoinHandle { f.pad("JoinHandle { .. }") } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -// Please maintain order from least to most priority for the derived `Ord` impl. -pub enum QoSClass { - // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55 - // - /// TLDR: invisible maintenance tasks - /// - /// Contract: - /// - /// * **You do not care about how long it takes for work to finish.** - /// * **You do not care about work being deferred temporarily.** - /// (e.g. if the device’s battery is in a critical state) - /// - /// Examples: - /// - /// * in a video editor: - /// creating periodic backups of project files - /// * in a browser: - /// cleaning up cached sites which have not been accessed in a long time - /// * in a collaborative word processor: - /// creating a searchable index of all documents - /// - /// Use this QoS class for background tasks - /// which the user did not initiate themselves - /// and which are invisible to the user. - /// It is expected that this work will take significant time to complete: - /// minutes or even hours. - /// - /// This QoS class provides the most energy and thermally-efficient execution possible. - /// All other work is prioritized over background tasks. - Background, - - /// TLDR: tasks that don’t block using your app - /// - /// Contract: - /// - /// * **Your app remains useful even as the task is executing.** - /// - /// Examples: - /// - /// * in a video editor: - /// exporting a video to disk – - /// the user can still work on the timeline - /// * in a browser: - /// automatically extracting a downloaded zip file – - /// the user can still switch tabs - /// * in a collaborative word processor: - /// downloading images embedded in a document – - /// the user can still make edits - /// - /// Use this QoS class for tasks which - /// may or may not be initiated by the user, - /// but whose result is visible. - /// It is expected that this work will take a few seconds to a few minutes. - /// Typically your app will include a progress bar - /// for tasks using this class. - /// - /// This QoS class provides a balance between - /// performance, responsiveness and efficiency. - Utility, - - /// TLDR: tasks that block using your app - /// - /// Contract: - /// - /// * **You need this work to complete - /// before the user can keep interacting with your app.** - /// * **Your work will not take more than a few seconds to complete.** - /// - /// Examples: - /// - /// * in a video editor: - /// opening a saved project - /// * in a browser: - /// loading a list of the user’s bookmarks and top sites - /// when a new tab is created - /// * in a collaborative word processor: - /// running a search on the document’s content - /// - /// Use this QoS class for tasks which were initiated by the user - /// and block the usage of your app while they are in progress. - /// It is expected that this work will take a few seconds or less to complete; - /// not long enough to cause the user to switch to something else. - /// Your app will likely indicate progress on these tasks - /// through the display of placeholder content or modals. - /// - /// This QoS class is not energy-efficient. - /// Rather, it provides responsiveness - /// by prioritizing work above other tasks on the system - /// except for critical user-interactive work. - UserInitiated, - - /// TLDR: render loops and nothing else - /// - /// Contract: - /// - /// * **You absolutely need this work to complete immediately - /// or your app will appear to freeze.** - /// * **Your work will always complete virtually instantaneously.** - /// - /// Examples: - /// - /// * the main thread in a GUI application - /// * the update & render loop in a game - /// * a secondary thread which progresses an animation - /// - /// Use this QoS class for any work which, if delayed, - /// will make your user interface unresponsive. - /// It is expected that this work will be virtually instantaneous. - /// - /// This QoS class is not energy-efficient. - /// Specifying this class is a request to run with - /// nearly all available system CPU and I/O bandwidth even under contention. - UserInteractive, -} - -pub const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE; - -pub fn set_current_thread_qos_class(class: QoSClass) { - imp::set_current_thread_qos_class(class) -} - -pub fn get_current_thread_qos_class() -> Option { - imp::get_current_thread_qos_class() -} - -// All Apple platforms use XNU as their kernel -// and thus have the concept of QoS. -#[cfg(target_vendor = "apple")] -mod imp { - use super::QoSClass; - - pub(super) const IS_QOS_AVAILABLE: bool = true; - - pub(super) fn set_current_thread_qos_class(class: QoSClass) { - let c = match class { - QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, - QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, - QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, - QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, - }; - - let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; - - if code == 0 { - return; - } - - let errno = unsafe { *libc::__error() }; - - match errno { - libc::EPERM => { - // This thread has been excluded from the QoS system - // due to a previous call to a function such as `pthread_setschedparam` - // which is incompatible with QoS. - // - // Panic instead of returning an error - // to maintain the invariant that we only use QoS APIs. - panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") - } - - libc::EINVAL => { - // This is returned if we pass something other than a qos_class_t - // to `pthread_set_qos_class_self_np`. - // - // This is impossible, so again panic. - unreachable!( - "invalid qos_class_t value was passed to pthread_set_qos_class_self_np" - ) - } - - _ => { - // `pthread_set_qos_class_self_np`’s documentation - // does not mention any other errors. - unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") - } - } - } - - pub(super) fn get_current_thread_qos_class() -> Option { - let current_thread = unsafe { libc::pthread_self() }; - let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; - let code = unsafe { - libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) - }; - - if code != 0 { - // `pthread_get_qos_class_np`’s documentation states that - // an error value is placed into errno if the return code is not zero. - // However, it never states what errors are possible. - // Inspecting the source[0] shows that, as of this writing, it always returns zero. - // - // Whatever errors the function could report in future are likely to be - // ones which we cannot handle anyway - // - // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 - let errno = unsafe { *libc::__error() }; - unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); - } - - match qos_class_raw { - libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), - libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), - libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set - libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), - libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), - - libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { - // Using manual scheduling APIs causes threads to “opt out” of QoS. - // At this point they become incompatible with QoS, - // and as such have the “unspecified” QoS class. - // - // Panic instead of returning an error - // to maintain the invariant that we only use QoS APIs. - panic!("tried to get QoS of thread which has opted out of QoS") - } - } - } -} - -// FIXME: Windows has QoS APIs, we should use them! -#[cfg(not(target_vendor = "apple"))] -mod imp { - use super::QoSClass; - - pub(super) const IS_QOS_AVAILABLE: bool = false; - - pub(super) fn set_current_thread_qos_class(_: QoSClass) {} - - pub(super) fn get_current_thread_qos_class() -> Option { - None - } -} diff --git a/crates/stdx/src/thread/intent.rs b/crates/stdx/src/thread/intent.rs new file mode 100644 index 0000000000000..7b65db30cc5b0 --- /dev/null +++ b/crates/stdx/src/thread/intent.rs @@ -0,0 +1,287 @@ +//! An opaque façade around platform-specific QoS APIs. + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +// Please maintain order from least to most priority for the derived `Ord` impl. +pub enum ThreadIntent { + /// Any thread which does work that isn’t in the critical path of the user typing + /// (e.g. processing Go To Definition). + Worker, + + /// Any thread which does work caused by the user typing + /// (e.g. processing syntax highlighting). + LatencySensitive, +} + +impl ThreadIntent { + // These APIs must remain private; + // we only want consumers to set thread intent + // either during thread creation or using our pool impl. + + pub(super) fn apply_to_current_thread(self) { + let class = thread_intent_to_qos_class(self); + set_current_thread_qos_class(class); + } + + pub(super) fn assert_is_used_on_current_thread(self) { + if IS_QOS_AVAILABLE { + let class = thread_intent_to_qos_class(self); + assert_eq!(get_current_thread_qos_class(), Some(class)); + } + } +} + +use imp::QoSClass; + +const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE; + +fn set_current_thread_qos_class(class: QoSClass) { + imp::set_current_thread_qos_class(class) +} + +fn get_current_thread_qos_class() -> Option { + imp::get_current_thread_qos_class() +} + +fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass { + imp::thread_intent_to_qos_class(intent) +} + +// All Apple platforms use XNU as their kernel +// and thus have the concept of QoS. +#[cfg(target_vendor = "apple")] +mod imp { + use super::ThreadIntent; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + // Please maintain order from least to most priority for the derived `Ord` impl. + pub(super) enum QoSClass { + // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55 + // + /// TLDR: invisible maintenance tasks + /// + /// Contract: + /// + /// * **You do not care about how long it takes for work to finish.** + /// * **You do not care about work being deferred temporarily.** + /// (e.g. if the device’s battery is in a critical state) + /// + /// Examples: + /// + /// * in a video editor: + /// creating periodic backups of project files + /// * in a browser: + /// cleaning up cached sites which have not been accessed in a long time + /// * in a collaborative word processor: + /// creating a searchable index of all documents + /// + /// Use this QoS class for background tasks + /// which the user did not initiate themselves + /// and which are invisible to the user. + /// It is expected that this work will take significant time to complete: + /// minutes or even hours. + /// + /// This QoS class provides the most energy and thermally-efficient execution possible. + /// All other work is prioritized over background tasks. + Background, + + /// TLDR: tasks that don’t block using your app + /// + /// Contract: + /// + /// * **Your app remains useful even as the task is executing.** + /// + /// Examples: + /// + /// * in a video editor: + /// exporting a video to disk – + /// the user can still work on the timeline + /// * in a browser: + /// automatically extracting a downloaded zip file – + /// the user can still switch tabs + /// * in a collaborative word processor: + /// downloading images embedded in a document – + /// the user can still make edits + /// + /// Use this QoS class for tasks which + /// may or may not be initiated by the user, + /// but whose result is visible. + /// It is expected that this work will take a few seconds to a few minutes. + /// Typically your app will include a progress bar + /// for tasks using this class. + /// + /// This QoS class provides a balance between + /// performance, responsiveness and efficiency. + Utility, + + /// TLDR: tasks that block using your app + /// + /// Contract: + /// + /// * **You need this work to complete + /// before the user can keep interacting with your app.** + /// * **Your work will not take more than a few seconds to complete.** + /// + /// Examples: + /// + /// * in a video editor: + /// opening a saved project + /// * in a browser: + /// loading a list of the user’s bookmarks and top sites + /// when a new tab is created + /// * in a collaborative word processor: + /// running a search on the document’s content + /// + /// Use this QoS class for tasks which were initiated by the user + /// and block the usage of your app while they are in progress. + /// It is expected that this work will take a few seconds or less to complete; + /// not long enough to cause the user to switch to something else. + /// Your app will likely indicate progress on these tasks + /// through the display of placeholder content or modals. + /// + /// This QoS class is not energy-efficient. + /// Rather, it provides responsiveness + /// by prioritizing work above other tasks on the system + /// except for critical user-interactive work. + UserInitiated, + + /// TLDR: render loops and nothing else + /// + /// Contract: + /// + /// * **You absolutely need this work to complete immediately + /// or your app will appear to freeze.** + /// * **Your work will always complete virtually instantaneously.** + /// + /// Examples: + /// + /// * the main thread in a GUI application + /// * the update & render loop in a game + /// * a secondary thread which progresses an animation + /// + /// Use this QoS class for any work which, if delayed, + /// will make your user interface unresponsive. + /// It is expected that this work will be virtually instantaneous. + /// + /// This QoS class is not energy-efficient. + /// Specifying this class is a request to run with + /// nearly all available system CPU and I/O bandwidth even under contention. + UserInteractive, + } + + pub(super) const IS_QOS_AVAILABLE: bool = true; + + pub(super) fn set_current_thread_qos_class(class: QoSClass) { + let c = match class { + QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, + QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, + QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, + QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, + }; + + let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; + + if code == 0 { + return; + } + + let errno = unsafe { *libc::__error() }; + + match errno { + libc::EPERM => { + // This thread has been excluded from the QoS system + // due to a previous call to a function such as `pthread_setschedparam` + // which is incompatible with QoS. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") + } + + libc::EINVAL => { + // This is returned if we pass something other than a qos_class_t + // to `pthread_set_qos_class_self_np`. + // + // This is impossible, so again panic. + unreachable!( + "invalid qos_class_t value was passed to pthread_set_qos_class_self_np" + ) + } + + _ => { + // `pthread_set_qos_class_self_np`’s documentation + // does not mention any other errors. + unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") + } + } + } + + pub(super) fn get_current_thread_qos_class() -> Option { + let current_thread = unsafe { libc::pthread_self() }; + let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; + let code = unsafe { + libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) + }; + + if code != 0 { + // `pthread_get_qos_class_np`’s documentation states that + // an error value is placed into errno if the return code is not zero. + // However, it never states what errors are possible. + // Inspecting the source[0] shows that, as of this writing, it always returns zero. + // + // Whatever errors the function could report in future are likely to be + // ones which we cannot handle anyway + // + // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 + let errno = unsafe { *libc::__error() }; + unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); + } + + match qos_class_raw { + libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), + libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), + libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set + libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), + libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), + + libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { + // Using manual scheduling APIs causes threads to “opt out” of QoS. + // At this point they become incompatible with QoS, + // and as such have the “unspecified” QoS class. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to get QoS of thread which has opted out of QoS") + } + } + } + + pub(super) fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass { + match intent { + ThreadIntent::Worker => QoSClass::Utility, + ThreadIntent::LatencySensitive => QoSClass::UserInitiated, + } + } +} + +// FIXME: Windows has QoS APIs, we should use them! +#[cfg(not(target_vendor = "apple"))] +mod imp { + use super::ThreadIntent; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub(super) enum QoSClass { + Default, + } + + pub(super) const IS_QOS_AVAILABLE: bool = false; + + pub(super) fn set_current_thread_qos_class(_: QoSClass) {} + + pub(super) fn get_current_thread_qos_class() -> Option { + None + } + + pub(super) fn thread_intent_to_qos_class(_: ThreadIntent) -> QoSClass { + QoSClass::Default + } +} diff --git a/crates/stdx/src/thread/pool.rs b/crates/stdx/src/thread/pool.rs index b4ab9cb292ce4..2ddd7da74c291 100644 --- a/crates/stdx/src/thread/pool.rs +++ b/crates/stdx/src/thread/pool.rs @@ -1,6 +1,7 @@ //! [`Pool`] implements a basic custom thread pool //! inspired by the [`threadpool` crate](http://docs.rs/threadpool). -//! It allows the spawning of tasks under different QoS classes. +//! When you spawn a task you specify a thread intent +//! so the pool can schedule it to run on a thread with that intent. //! rust-analyzer uses this to prioritize work based on latency requirements. //! //! The thread pool is implemented entirely using @@ -13,10 +14,7 @@ use std::sync::{ use crossbeam_channel::{Receiver, Sender}; -use super::{ - get_current_thread_qos_class, set_current_thread_qos_class, Builder, JoinHandle, QoSClass, - IS_QOS_AVAILABLE, -}; +use super::{Builder, JoinHandle, ThreadIntent}; pub struct Pool { // `_handles` is never read: the field is present @@ -32,32 +30,32 @@ pub struct Pool { } struct Job { - requested_qos_class: QoSClass, + requested_intent: ThreadIntent, f: Box, } impl Pool { pub fn new(threads: usize) -> Pool { const STACK_SIZE: usize = 8 * 1024 * 1024; - const INITIAL_QOS_CLASS: QoSClass = QoSClass::Utility; + const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker; let (job_sender, job_receiver) = crossbeam_channel::unbounded(); let extant_tasks = Arc::new(AtomicUsize::new(0)); let mut handles = Vec::with_capacity(threads); for _ in 0..threads { - let handle = Builder::new(INITIAL_QOS_CLASS) + let handle = Builder::new(INITIAL_INTENT) .stack_size(STACK_SIZE) .name("Worker".into()) .spawn({ let extant_tasks = Arc::clone(&extant_tasks); let job_receiver: Receiver = job_receiver.clone(); move || { - let mut current_qos_class = INITIAL_QOS_CLASS; + let mut current_intent = INITIAL_INTENT; for job in job_receiver { - if job.requested_qos_class != current_qos_class { - set_current_thread_qos_class(job.requested_qos_class); - current_qos_class = job.requested_qos_class; + if job.requested_intent != current_intent { + job.requested_intent.apply_to_current_thread(); + current_intent = job.requested_intent; } extant_tasks.fetch_add(1, Ordering::SeqCst); (job.f)(); @@ -73,19 +71,18 @@ impl Pool { Pool { _handles: handles, extant_tasks, job_sender } } - pub fn spawn(&self, qos_class: QoSClass, f: F) + pub fn spawn(&self, intent: ThreadIntent, f: F) where F: FnOnce() + Send + 'static, { let f = Box::new(move || { - if IS_QOS_AVAILABLE { - debug_assert_eq!(get_current_thread_qos_class(), Some(qos_class)); + if cfg!(debug_assertions) { + intent.assert_is_used_on_current_thread(); } - f() }); - let job = Job { requested_qos_class: qos_class, f }; + let job = Job { requested_intent: intent, f }; self.job_sender.send(job).unwrap(); } diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 26f7a9fc42359..abfc51dfec66f 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle { fn spawn(sender: loader::Sender) -> NotifyHandle { let actor = NotifyActor::new(sender); let (sender, receiver) = unbounded::(); - let thread = stdx::thread::Builder::new(stdx::thread::QoSClass::Utility) + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("VfsLoader".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); From 9ebaa85d374aa7e8cb07534235134b0074260f5b Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Tue, 23 May 2023 17:55:40 +0900 Subject: [PATCH 531/806] Split test module for metavariable expressions --- .../hir-def/src/macro_expansion_tests/mbe.rs | 87 +----------------- .../macro_expansion_tests/mbe/metavar_expr.rs | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+), 86 deletions(-) create mode 100644 crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index c056c077a5683..553ffe3d0b88b 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -4,6 +4,7 @@ mod tt_conversion; mod matching; mod meta_syntax; +mod metavar_expr; mod regression; use expect_test::expect; @@ -1614,92 +1615,6 @@ struct Foo; ) } -#[test] -fn test_dollar_dollar() { - check( - r#" -macro_rules! register_struct { ($Struct:ident) => { - macro_rules! register_methods { ($$($method:ident),*) => { - macro_rules! implement_methods { ($$$$($$val:expr),*) => { - struct $Struct; - impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} - }} - }} -}} - -register_struct!(Foo); -register_methods!(alpha, beta); -implement_methods!(1, 2, 3); -"#, - expect![[r#" -macro_rules! register_struct { ($Struct:ident) => { - macro_rules! register_methods { ($$($method:ident),*) => { - macro_rules! implement_methods { ($$$$($$val:expr),*) => { - struct $Struct; - impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} - }} - }} -}} - -macro_rules !register_methods { - ($($method: ident), *) = > { - macro_rules!implement_methods { - ($$($val: expr), *) = > { - struct Foo; - impl Foo { - $(fn $method()-> &'static[u32] { - &[$$($$val), *] - } - )* - } - } - } - } -} -macro_rules !implement_methods { - ($($val: expr), *) = > { - struct Foo; - impl Foo { - fn alpha()-> &'static[u32] { - &[$($val), *] - } - fn beta()-> &'static[u32] { - &[$($val), *] - } - } - } -} -struct Foo; -impl Foo { - fn alpha() -> &'static[u32] { - &[1, 2, 3] - } - fn beta() -> &'static[u32] { - &[1, 2, 3] - } -} -"#]], - ) -} - -#[test] -fn test_metavar_exprs() { - check( - r#" -macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); -} -const _: i32 = m!(a b c); - "#, - expect![[r#" -macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); -} -const _: i32 = -0--1--2; - "#]], - ); -} - #[test] fn test_punct_without_space() { // Puncts are "glued" greedily. diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs new file mode 100644 index 0000000000000..ae138529ea647 --- /dev/null +++ b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -0,0 +1,91 @@ +//! Tests for RFC 3086 metavariable expressions. + +use expect_test::expect; + +use crate::macro_expansion_tests::check; + +#[test] +fn test_dollar_dollar() { + check( + r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +register_struct!(Foo); +register_methods!(alpha, beta); +implement_methods!(1, 2, 3); +"#, + expect![[r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +macro_rules !register_methods { + ($($method: ident), *) = > { + macro_rules!implement_methods { + ($$($val: expr), *) = > { + struct Foo; + impl Foo { + $(fn $method()-> &'static[u32] { + &[$$($$val), *] + } + )* + } + } + } + } +} +macro_rules !implement_methods { + ($($val: expr), *) = > { + struct Foo; + impl Foo { + fn alpha()-> &'static[u32] { + &[$($val), *] + } + fn beta()-> &'static[u32] { + &[$($val), *] + } + } + } +} +struct Foo; +impl Foo { + fn alpha() -> &'static[u32] { + &[1, 2, 3] + } + fn beta() -> &'static[u32] { + &[1, 2, 3] + } +} +"#]], + ) +} + +#[test] +fn test_metavar_exprs() { + check( + r#" +macro_rules! m { + ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); +} +const _: i32 = m!(a b c); + "#, + expect![[r#" +macro_rules! m { + ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); +} +const _: i32 = -0--1--2; + "#]], + ); +} From 0d4d1d7e3b820c241e8197926b62830b9d2d3b24 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 28 May 2023 19:54:36 +0900 Subject: [PATCH 532/806] Implement `${count()}` metavariable expression --- .../macro_expansion_tests/mbe/metavar_expr.rs | 220 ++++++++++++++++++ crates/mbe/src/benchmark.rs | 2 +- crates/mbe/src/expander/matcher.rs | 8 +- crates/mbe/src/expander/transcriber.rs | 128 ++++++++-- crates/mbe/src/lib.rs | 21 ++ crates/mbe/src/parser.rs | 32 ++- crates/mbe/src/tt_iter.rs | 7 - 7 files changed, 389 insertions(+), 29 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs index ae138529ea647..967b5ad36babf 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -89,3 +89,223 @@ const _: i32 = -0--1--2; "#]], ); } + +#[test] +fn count_basic() { + check( + r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t)} + } +} + +fn test() { + m!(); + m!(a); + m!(a, a); +} +"#, + expect![[r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t)} + } +} + +fn test() { + 0; + 1; + 2; +} +"#]], + ); +} + +#[test] +fn count_with_depth() { + check( + r#" +macro_rules! foo { + ($( $( $($t:ident)* ),* );*) => { + $( + { + let depth_none = ${count(t)}; + let depth_zero = ${count(t, 0)}; + let depth_one = ${count(t, 1)}; + } + )* + } +} + +fn bar() { + foo!( + a a a, a, a a; + a a a + ) +} +"#, + expect![[r#" +macro_rules! foo { + ($( $( $($t:ident)* ),* );*) => { + $( + { + let depth_none = ${count(t)}; + let depth_zero = ${count(t, 0)}; + let depth_one = ${count(t, 1)}; + } + )* + } +} + +fn bar() { + { + let depth_none = 6; + let depth_zero = 3; + let depth_one = 6; + } { + let depth_none = 3; + let depth_zero = 1; + let depth_one = 3; + } +} +"#]], + ); +} + +#[test] +fn count_depth_out_of_bounds() { + check( + r#" +macro_rules! foo { + ($($t:ident)*) => { ${count(t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } +} +macro_rules! bar { + ($($t:ident)*) => { ${count(t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } +} + +fn test() { + foo!(a b); + foo!(1 2; 3); + bar!(a b); + bar!(1 2; 3); +} +"#, + expect![[r#" +macro_rules! foo { + ($($t:ident)*) => { ${count(t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } +} +macro_rules! bar { + ($($t:ident)*) => { ${count(t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } +} + +fn test() { + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; +} +"#]], + ); +} + +#[test] +fn misplaced_count() { + check( + r#" +macro_rules! foo { + ($($t:ident)*) => { $(${count(t)})* }; + ($l:literal) => { ${count(l)} } +} + +fn test() { + foo!(a b c); + foo!(1); +} +"#, + expect![[r#" +macro_rules! foo { + ($($t:ident)*) => { $(${count(t)})* }; + ($l:literal) => { ${count(l)} } +} + +fn test() { + /* error: ${count} misplaced */; + /* error: ${count} misplaced */; +} +"#]], + ); +} + +#[test] +fn malformed_count() { + check( + r#" +macro_rules! too_many_args { + ($($t:ident)*) => { ${count(t, 1, leftover)} } +} +macro_rules! depth_suffixed { + ($($t:ident)*) => { ${count(t, 0usize)} } +} +macro_rules! depth_too_large { + ($($t:ident)*) => { ${count(t, 18446744073709551616)} } +} + +fn test() { + too_many_args!(); + depth_suffixed!(); + depth_too_large!(); +} +"#, + expect![[r#" +macro_rules! too_many_args { + ($($t:ident)*) => { ${count(t, 1, leftover)} } +} +macro_rules! depth_suffixed { + ($($t:ident)*) => { ${count(t, 0usize)} } +} +macro_rules! depth_too_large { + ($($t:ident)*) => { ${count(t, 18446744073709551616)} } +} + +fn test() { + /* error: invalid macro definition: invalid metavariable expression */; + /* error: invalid macro definition: invalid metavariable expression */; + /* error: invalid macro definition: invalid metavariable expression */; +} +"#]], + ); +} + +#[test] +fn count_interaction_with_empty_binding() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t, 100)} + } +} + +fn test() { + m!(); +} +"#, + expect![[r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t, 100)} + } +} + +fn test() { + 0; +} +"#]], + ); +} diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index d640eea19a2e7..d28dd17def378 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -195,7 +195,7 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri }); parent.token_trees.push(subtree.into()); } - Op::Ignore { .. } | Op::Index { .. } => {} + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {} }; // Simple linear congruential generator for deterministic result diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 0ab5c8c3c9e73..474826079d733 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -567,7 +567,9 @@ fn match_loop_inner<'t>( item.is_error = true; error_items.push(item); } - OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {} + OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => { + stdx::never!("metavariable expression in lhs found"); + } OpDelimited::Open => { if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) { item.dot.next(); @@ -811,7 +813,9 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) Op::Var { name, .. } => collector_fun(name.clone()), Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens), Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens), - Op::Ignore { .. } | Op::Index { .. } | Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => { + Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {} + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => { + stdx::never!("metavariable expression in lhs found"); } } } diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index dffb40d4bc886..6161af185871d 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -7,7 +7,7 @@ use crate::{ expander::{Binding, Bindings, Fragment}, parser::{MetaVarKind, Op, RepeatKind, Separator}, tt::{self, Delimiter}, - ExpandError, ExpandResult, MetaTemplate, + CountError, ExpandError, ExpandResult, MetaTemplate, }; impl Bindings { @@ -15,13 +15,23 @@ impl Bindings { self.inner.contains_key(name) } - fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result { + fn get(&self, name: &str) -> Result<&Binding, ExpandError> { + match self.inner.get(name) { + Some(binding) => Ok(binding), + None => Err(ExpandError::binding_error(format!("could not find binding `{name}`"))), + } + } + + fn get_fragment( + &self, + name: &str, + nesting: &mut [NestingState], + ) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } - let mut b: &Binding = - self.inner.get(name).ok_or_else(|| binding_err!("could not find binding `{name}`"))?; + let mut b = self.get(name)?; for nesting_state in nesting.iter_mut() { nesting_state.hit = true; b = match b { @@ -133,7 +143,7 @@ fn expand_subtree( // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation let start_elements = arena.len(); let mut err = None; - for op in template.iter() { + 'ops: for op in template.iter() { match op { Op::Literal(it) => arena.push(tt::Leaf::from(it.clone()).into()), Op::Ident(it) => arena.push(tt::Leaf::from(it.clone()).into()), @@ -161,13 +171,12 @@ fn expand_subtree( } Op::Ignore { name, id } => { // Expand the variable, but ignore the result. This registers the repetition count. + // FIXME: Any emitted errors are dropped. expand_var(ctx, name, *id); } Op::Index { depth } => { - let index = ctx - .nesting - .get(ctx.nesting.len() - 1 - (*depth as usize)) - .map_or(0, |nest| nest.idx); + let index = + ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); arena.push( tt::Leaf::Literal(tt::Literal { text: index.to_string().into(), @@ -176,6 +185,65 @@ fn expand_subtree( .into(), ); } + Op::Count { name, depth } => { + let mut binding = match ctx.bindings.get(name.as_str()) { + Ok(b) => b, + Err(e) => { + if err.is_none() { + err = Some(e); + } + continue; + } + }; + for state in ctx.nesting.iter_mut() { + state.hit = true; + match binding { + Binding::Fragment(_) | Binding::Missing(_) => { + // `count()` will report an error. + break; + } + Binding::Nested(bs) => { + if let Some(b) = bs.get(state.idx) { + binding = b; + } else { + state.at_end = true; + continue 'ops; + } + } + Binding::Empty => { + state.at_end = true; + // FIXME: Breaking here and proceeding to `count()` isn't the most + // correct thing to do here. This could be a binding of some named + // fragment which we don't know the depth of, so `count()` will just + // return 0 for this no matter what `depth` is. See test + // `count_interaction_with_empty_binding` for example. + break; + } + } + } + + let c = match count(ctx, binding, 0, *depth) { + Ok(c) => c, + Err(e) => { + // XXX: It *might* make sense to emit a dummy integer value like `0` here. + // That would type inference a bit more robust in cases like + // `v[${count(t)}]` where index doesn't matter, but also coult also lead to + // wrong infefrence for cases like `tup.${count(t)}` where index itself + // does matter. + if err.is_none() { + err = Some(e.into()); + } + continue; + } + }; + arena.push( + tt::Leaf::Literal(tt::Literal { + text: c.to_string().into(), + span: tt::TokenId::unspecified(), + }) + .into(), + ); + } } } // drain the elements added in this instance of expand_subtree @@ -218,12 +286,9 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe .into(); ExpandResult::ok(Fragment::Tokens(tt)) } else { - ctx.bindings.get(v, &mut ctx.nesting).map_or_else( + ctx.bindings.get_fragment(v, &mut ctx.nesting).map_or_else( |e| ExpandResult { - value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::unspecified(), - token_trees: vec![], - })), + value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty())), err: Some(e), }, ExpandResult::ok, @@ -245,6 +310,7 @@ fn expand_repeat( let limit = 65536; let mut has_seps = 0; let mut counter = 0; + let mut err = None; loop { let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, None, arena); @@ -272,6 +338,7 @@ fn expand_repeat( } if e.is_some() { + err = err.or(e); continue; } @@ -317,7 +384,7 @@ fn expand_repeat( err: Some(ExpandError::UnexpectedToken), }; } - ExpandResult::ok(Fragment::Tokens(tt)) + ExpandResult { value: Fragment::Tokens(tt), err } } fn push_fragment(buf: &mut Vec, fragment: Fragment) { @@ -343,3 +410,34 @@ fn push_subtree(buf: &mut Vec, tt: tt::Subtree) { _ => buf.push(tt.into()), } } + +/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth +/// defined by the metavar expression. +fn count( + ctx: &ExpandCtx<'_>, + binding: &Binding, + our_depth: usize, + count_depth: Option, +) -> Result { + match binding { + Binding::Nested(bs) => match count_depth { + None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(), + Some(0) => Ok(bs.len()), + Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(), + }, + Binding::Empty => Ok(0), + Binding::Fragment(_) | Binding::Missing(_) => { + if our_depth == 0 { + // `${count(t)}` is placed inside the innermost repetition. This includes cases + // where `t` is not a repeated fragment. + Err(CountError::Misplaced) + } else if count_depth.is_none() { + Ok(1) + } else { + // We've reached at the innermost repeated fragment, but the user wants us to go + // further! + Err(CountError::OutOfBounds) + } + } + } +} diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 80352aa4adc1e..5ef20ff8a9b05 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -19,6 +19,7 @@ mod benchmark; mod token_map; use ::tt::token_id as tt; +use stdx::impl_from; use std::fmt; @@ -77,8 +78,11 @@ pub enum ExpandError { LimitExceeded, NoMatchingRule, UnexpectedToken, + CountError(CountError), } +impl_from!(CountError for ExpandError); + impl ExpandError { fn binding_error(e: impl Into>) -> ExpandError { ExpandError::BindingError(Box::new(e.into())) @@ -94,6 +98,23 @@ impl fmt::Display for ExpandError { ExpandError::ConversionError => f.write_str("could not convert tokens"), ExpandError::LimitExceeded => f.write_str("Expand exceed limit"), ExpandError::LeftoverTokens => f.write_str("leftover tokens"), + ExpandError::CountError(e) => e.fmt(f), + } + } +} + +// FIXME: Showing these errors could be nicer. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum CountError { + OutOfBounds, + Misplaced, +} + +impl fmt::Display for CountError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CountError::OutOfBounds => f.write_str("${count} out of bounds"), + CountError::Misplaced => f.write_str("${count} misplaced"), } } } diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 0fbf832b06df2..7a143e7466a93 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -52,7 +52,8 @@ impl MetaTemplate { pub(crate) enum Op { Var { name: SmolStr, kind: Option, id: tt::TokenId }, Ignore { name: SmolStr, id: tt::TokenId }, - Index { depth: u32 }, + Index { depth: usize }, + Count { name: SmolStr, depth: Option }, Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option }, Subtree { tokens: MetaTemplate, delimiter: tt::Delimiter }, Literal(tt::Literal), @@ -295,9 +296,13 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result { let ident = args.expect_ident()?; Op::Ignore { name: ident.text.clone(), id: ident.span } } - "index" => { - let depth = if args.len() == 0 { 0 } else { args.expect_u32_literal()? }; - Op::Index { depth } + "index" => Op::Index { depth: parse_depth(&mut args)? }, + "count" => { + let ident = args.expect_ident()?; + // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug + // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904. + let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; + Op::Count { name: ident.text.clone(), depth } } _ => return Err(()), }; @@ -308,3 +313,22 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result { Ok(op) } + +fn parse_depth(src: &mut TtIter<'_>) -> Result { + if src.len() == 0 { + Ok(0) + } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { + // Suffixes are not allowed. + lit.text.parse().map_err(|_| ()) + } else { + Err(()) + } +} + +fn try_eat_comma(src: &mut TtIter<'_>) -> bool { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { + let _ = src.next(); + return true; + } + false +} diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index f744481f3aecb..59dbf156800d4 100644 --- a/crates/mbe/src/tt_iter.rs +++ b/crates/mbe/src/tt_iter.rs @@ -73,13 +73,6 @@ impl<'a> TtIter<'a> { } } - pub(crate) fn expect_u32_literal(&mut self) -> Result { - match self.expect_literal()? { - tt::Leaf::Literal(lit) => lit.text.parse().map_err(drop), - _ => Err(()), - } - } - pub(crate) fn expect_single_punct(&mut self) -> Result<&'a tt::Punct, ()> { match self.expect_leaf()? { tt::Leaf::Punct(it) => Ok(it), From cea84427e00ad76b8400d45a84347b65d27dcef3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 22:12:22 +0200 Subject: [PATCH 533/806] Allow setting cfgs --- crates/cfg/src/lib.rs | 2 +- crates/project-model/src/cargo_workspace.rs | 39 +---------------- crates/project-model/src/lib.rs | 2 +- crates/project-model/src/tests.rs | 22 +++++----- crates/project-model/src/workspace.rs | 48 +++++++-------------- crates/rust-analyzer/src/config.rs | 35 +++++++++++++-- docs/user/generated_config.adoc | 7 ++- editors/code/package.json | 7 ++- 8 files changed, 73 insertions(+), 89 deletions(-) diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 30709c968dacf..495119d5519c3 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -86,7 +86,7 @@ impl CfgOptions { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. enable: Vec, diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index e3fdeb448db5e..671760d4deb83 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -1,6 +1,5 @@ //! See [`CargoWorkspace`]. -use std::iter; use std::path::PathBuf; use std::str::from_utf8; use std::{ops, process::Command}; @@ -58,20 +57,6 @@ pub enum RustLibSource { Discover, } -/// Crates to disable `#[cfg(test)]` on. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum UnsetTestCrates { - None, - Only(Vec), - All, -} - -impl Default for UnsetTestCrates { - fn default() -> Self { - Self::None - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum CargoFeatures { All, @@ -100,8 +85,7 @@ pub struct CargoConfig { pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, - /// crates to disable `#[cfg(test)]` on - pub unset_test_crates: UnsetTestCrates, + pub cfg_overrides: CfgOverrides, /// Invoke `cargo check` through the RUSTC_WRAPPER. pub wrap_rustc_in_build_scripts: bool, /// The command to run instead of `cargo check` for building build scripts. @@ -114,27 +98,6 @@ pub struct CargoConfig { pub invocation_location: InvocationLocation, } -impl CargoConfig { - pub fn cfg_overrides(&self) -> CfgOverrides { - match &self.unset_test_crates { - UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()), - UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective( - unset_test_crates - .iter() - .cloned() - .zip(iter::repeat_with(|| { - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]) - .unwrap() - })) - .collect(), - ), - UnsetTestCrates::All => CfgOverrides::Wildcard( - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(), - ), - } - } -} - pub type Package = Idx; pub type Target = Idx; diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 70cb71ae3bde8..61acc646f8101 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -44,7 +44,7 @@ pub use crate::{ build_scripts::WorkspaceBuildScripts, cargo_workspace::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates, + RustLibSource, Target, TargetData, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index c3c654ddb6f5e..7815b9dda77f2 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -158,9 +158,10 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { #[test] fn cargo_hello_world_project_model_with_wildcard_overrides() { - let cfg_overrides = CfgOverrides::Wildcard( - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - ); + let cfg_overrides = CfgOverrides { + global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + selective: Default::default(), + }; let (crate_graph, _proc_macros) = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( @@ -173,14 +174,13 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { #[test] fn cargo_hello_world_project_model_with_selective_overrides() { - let cfg_overrides = { - CfgOverrides::Selective( - std::iter::once(( - "libc".to_owned(), - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - )) - .collect(), - ) + let cfg_overrides = CfgOverrides { + global: Default::default(), + selective: std::iter::once(( + "libc".to_owned(), + CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + )) + .collect(), }; let (crate_graph, _proc_macros) = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 4adaa6d86ee34..a695bc1cca78c 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -28,29 +28,17 @@ use crate::{ }; /// A set of cfg-overrides per crate. -/// -/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates, -/// without having to first obtain a list of all crates. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum CfgOverrides { - /// A single global set of overrides matching all crates. - Wildcard(CfgDiff), +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct CfgOverrides { + /// A global set of overrides matching all crates. + pub global: CfgDiff, /// A set of overrides matching specific crates. - Selective(FxHashMap), -} - -impl Default for CfgOverrides { - fn default() -> Self { - Self::Selective(FxHashMap::default()) - } + pub selective: FxHashMap, } impl CfgOverrides { pub fn len(&self) -> usize { - match self { - CfgOverrides::Wildcard(_) => 1, - CfgOverrides::Selective(hash_map) => hash_map.len(), - } + self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::() } } @@ -292,7 +280,7 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); - let cfg_overrides = config.cfg_overrides(); + let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( Some(&cargo_toml), config.target.as_deref(), @@ -886,12 +874,10 @@ fn cargo_to_crate_graph( cfg_options.insert_atom("test".into()); } - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name), + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); }; - - if let Some(overrides) = overrides { + if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) { // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while // working on rust-lang/rust as that's the only time it appears outside sysroot). @@ -899,7 +885,7 @@ fn cargo_to_crate_graph( // A more ideal solution might be to reanalyze crates based on where the cursor is and // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); + cfg_options.apply_diff(diff.clone()); }; cfg_options }); @@ -1109,14 +1095,10 @@ fn handle_rustc_crates( let mut cfg_options = cfg_options.clone(); - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => { - cfg_overrides.get(&rustc_workspace[pkg].name) - } + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); }; - - if let Some(overrides) = overrides { + if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) { // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while // working on rust-lang/rust as that's the only time it appears outside sysroot). @@ -1124,7 +1106,7 @@ fn handle_rustc_crates( // A more ideal solution might be to reanalyze crates based on where the cursor is and // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); + cfg_options.apply_diff(diff.clone()); }; for &tgt in rustc_workspace[pkg].targets.iter() { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index b1d019cb113c7..649ac90fc6e07 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -9,6 +9,7 @@ use std::{fmt, iter, ops::Not, path::PathBuf}; +use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, @@ -23,7 +24,6 @@ use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, - UnsetTestCrates, }; use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de::DeserializeOwned, Deserialize}; @@ -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", + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap = "{}", /// Extra arguments that are passed to every cargo invocation. cargo_extraArgs: Vec = "[]", /// Extra environment variables that will be set when running cargo, rustc @@ -128,7 +130,7 @@ config_data! { // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work // than `checkOnSave_target` cargo_target: Option = "null", - /// Unsets `#[cfg(test)]` for the specified crates. + /// Unsets the implicit `#[cfg(test)]` for the specified crates. cargo_unsetTest: Vec = "[\"core\"]", /// Run the check command for diagnostics on save. @@ -1189,7 +1191,34 @@ impl Config { sysroot, sysroot_src, rustc_source, - unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), + cfg_overrides: project_model::CfgOverrides { + global: CfgDiff::new( + self.data + .cargo_cfgs + .iter() + .map(|(key, val)| { + if val.is_empty() { + CfgAtom::Flag(key.into()) + } else { + CfgAtom::KeyValue { key: key.into(), value: val.into() } + } + }) + .collect(), + vec![], + ) + .unwrap(), + selective: self + .data + .cargo_unsetTest + .iter() + .map(|it| { + ( + it.clone(), + CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(), + ) + }) + .collect(), + }, wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { InvocationStrategy::Once => project_model::InvocationStrategy::Once, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index c2f8c6c754f08..dc97366eef1aa 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -71,6 +71,11 @@ cargo check --quiet --workspace --message-format=json --all-targets Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to avoid checking unnecessary things. -- +[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs (default: `{}`):: ++ +-- +List of cfg options to enable with the given values. +-- [[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`):: + -- @@ -120,7 +125,7 @@ Compilation target override (target triple). [[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`):: + -- -Unsets `#[cfg(test)]` for the specified crates. +Unsets the implicit `#[cfg(test)]` for the specified crates. -- [[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 390508b883e4f..aa63c40c0d299 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -553,6 +553,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.cargo.cfgs": { + "markdownDescription": "List of cfg options to enable with the given values.", + "default": {}, + "type": "object" + }, "rust-analyzer.cargo.extraArgs": { "markdownDescription": "Extra arguments that are passed to every cargo invocation.", "default": [], @@ -617,7 +622,7 @@ ] }, "rust-analyzer.cargo.unsetTest": { - "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.", + "markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.", "default": [ "core" ], From ffc2bc83b024a547ceecf3117149c1fbe9f560df Mon Sep 17 00:00:00 2001 From: Renato Lochetti Date: Sun, 28 May 2023 12:53:03 +0100 Subject: [PATCH 534/806] Fixing `invalid_regex` with invalid UTF8. Also, adding more test cases --- clippy_lints/src/regex.rs | 2 +- tests/ui/regex.rs | 7 +++++++ tests/ui/regex.stderr | 30 ++++++++++++++++++------------ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index ef19c6f461729..674f8bf4c0f30 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -177,7 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { } fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build(); + let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(utf8).build(); if let ExprKind::Lit(lit) = expr.kind { if let LitKind::Str(ref r, style) = lit.node { diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index a5f79b139bc2a..1c8e47ab59494 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -42,6 +42,11 @@ fn syntax_error() { let escaped_string_span = Regex::new("\\b\\c"); let aux_span = Regex::new("(?ixi)"); + + let should_not_lint = Regex::new("(?u)."); + let should_not_lint = BRegex::new("(?u)."); + let invalid_utf8_should_not_lint = BRegex::new("(?-u)."); + let invalid_utf8_should_lint = Regex::new("(?-u)."); } fn trivial_regex() { @@ -71,6 +76,8 @@ fn trivial_regex() { // non-trivial regexes let non_trivial_dot = Regex::new("a.b"); let non_trivial_dot_builder = RegexBuilder::new("a.b"); + let non_trivial_dot = Regex::new("."); + let non_trivial_dot = BRegex::new("."); let non_trivial_eq = Regex::new("^foo|bar$"); let non_trivial_starts_with = Regex::new("^foo|bar"); let non_trivial_ends_with = Regex::new("^foo|bar"); diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 6b8a772e7f0d5..1e8a21283cd95 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -99,8 +99,14 @@ error: regex syntax error: duplicate flag LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ +error: regex syntax error: pattern can match invalid UTF-8 + --> $DIR/regex.rs:49:53 + | +LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); + | ^ + error: trivial regex - --> $DIR/regex.rs:48:33 + --> $DIR/regex.rs:53:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -108,7 +114,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:50:48 + --> $DIR/regex.rs:55:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +122,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:52:42 + --> $DIR/regex.rs:57:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -124,7 +130,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:59:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -132,7 +138,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:56:39 + --> $DIR/regex.rs:61:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -140,7 +146,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:58:39 + --> $DIR/regex.rs:63:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -148,7 +154,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:60:40 + --> $DIR/regex.rs:65:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -156,7 +162,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:63:36 + --> $DIR/regex.rs:68:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -164,7 +170,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:65:36 + --> $DIR/regex.rs:70:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -172,7 +178,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:67:36 + --> $DIR/regex.rs:72:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -180,12 +186,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:69:44 + --> $DIR/regex.rs:74:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors From 6b46095980bf73512f5ed06d0659d263a4c990df Mon Sep 17 00:00:00 2001 From: Luna Razzaghipour Date: Sun, 28 May 2023 22:10:24 +1000 Subject: [PATCH 535/806] Make formatting a latency-sensitive request --- crates/rust-analyzer/src/main_loop.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 34947fcd6f95b..19c49a23000ea 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -695,6 +695,14 @@ impl GlobalState { .on_latency_sensitive::( handlers::handle_semantic_tokens_range, ) + // Formatting is not caused by the user typing, + // but it does qualify as latency-sensitive + // because a delay before formatting is applied + // can be confusing for the user. + .on_latency_sensitive::(handlers::handle_formatting) + .on_latency_sensitive::( + handlers::handle_range_formatting, + ) // All other request handlers .on::(handlers::fetch_dependency_list) .on::(handlers::handle_analyzer_status) @@ -730,8 +738,6 @@ impl GlobalState { .on::(handlers::handle_prepare_rename) .on::(handlers::handle_rename) .on::(handlers::handle_references) - .on::(handlers::handle_formatting) - .on::(handlers::handle_range_formatting) .on::(handlers::handle_document_highlight) .on::(handlers::handle_call_hierarchy_prepare) .on::( From bbd9e41606cba8efb02e356f78b7f1c8cfc04a94 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 22:41:32 +0200 Subject: [PATCH 536/806] Don't add --all-targets to runnables for no-std crates --- crates/hir-def/src/nameres.rs | 10 ++++++++++ crates/hir-def/src/nameres/collector.rs | 20 ++++++++++++++----- crates/hir-expand/src/name.rs | 2 ++ crates/ide/src/lib.rs | 5 +++++ crates/rust-analyzer/src/cargo_target_spec.rs | 4 +++- crates/rust-analyzer/src/handlers/request.rs | 19 +++++++++++------- 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 8aceb4952a546..ccb9bed5c5017 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -127,6 +127,8 @@ pub struct DefMap { unstable_features: FxHashSet, /// #[rustc_coherence_is_core] rustc_coherence_is_core: bool, + no_core: bool, + no_std: bool, edition: Edition, recursion_limit: Option, @@ -294,6 +296,8 @@ impl DefMap { unstable_features: FxHashSet::default(), diagnostics: Vec::new(), rustc_coherence_is_core: false, + no_core: false, + no_std: false, } } @@ -331,6 +335,10 @@ impl DefMap { self.rustc_coherence_is_core } + pub fn is_no_std(&self) -> bool { + self.no_std || self.no_core + } + pub fn root(&self) -> LocalModuleId { self.root } @@ -528,6 +536,8 @@ impl DefMap { prelude: _, root: _, rustc_coherence_is_core: _, + no_core: _, + no_std: _, } = self; extern_prelude.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index c49bb248a7b08..64caf26299ca0 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -291,8 +291,6 @@ impl DefCollector<'_> { let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); - self.inject_prelude(&attrs); - // Process other crate-level attributes. for attr in &*attrs { if let Some(cfg) = attr.cfg() { @@ -321,6 +319,16 @@ impl DefCollector<'_> { continue; } + if *attr_name == hir_expand::name![no_core] { + self.def_map.no_core = true; + continue; + } + + if *attr_name == hir_expand::name![no_std] { + self.def_map.no_std = true; + continue; + } + if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { self.def_map.rustc_coherence_is_core = true; continue; @@ -359,6 +367,8 @@ impl DefCollector<'_> { } } + self.inject_prelude(); + ModCollector { def_collector: self, macro_depth: 0, @@ -517,15 +527,15 @@ impl DefCollector<'_> { } } - fn inject_prelude(&mut self, crate_attrs: &Attrs) { + fn inject_prelude(&mut self) { // See compiler/rustc_builtin_macros/src/standard_library_imports.rs - if crate_attrs.by_key("no_core").exists() { + if self.def_map.no_core { // libcore does not get a prelude. return; } - let krate = if crate_attrs.by_key("no_std").exists() { + let krate = if self.def_map.no_std { name![core] } else { let std = name![std]; diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 0e95673dbd439..f8dbb842775c3 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -366,6 +366,8 @@ pub mod known { crate_type, derive, global_allocator, + no_core, + no_std, test, test_case, recursion_limit, diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 4e5f01e716691..c02dbc60a339b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -531,6 +531,11 @@ impl Analysis { self.with_db(|db| db.crate_graph()[crate_id].edition) } + /// Returns true if this crate has `no_std` or `no_core` specified. + pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable { + self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std()) + } + /// Returns the root file of the given crate. pub fn crate_root(&self, crate_id: CrateId) -> Cancellable { self.with_db(|db| db.crate_graph()[crate_id].root_file_id) diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 3035dc333080e..c7b84c41b33e9 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -3,7 +3,7 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; -use ide::{Cancellable, FileId, RunnableKind, TestId}; +use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId}; use project_model::{self, CargoFeatures, ManifestPath, TargetKind}; use rustc_hash::FxHashSet; use vfs::AbsPathBuf; @@ -21,6 +21,7 @@ pub(crate) struct CargoTargetSpec { pub(crate) package: String, pub(crate) target: String, pub(crate) target_kind: TargetKind, + pub(crate) crate_id: CrateId, pub(crate) required_features: Vec, pub(crate) features: FxHashSet, } @@ -142,6 +143,7 @@ impl CargoTargetSpec { target_kind: target_data.kind, required_features: target_data.required_features.clone(), features: package_data.features.keys().cloned().collect(), + crate_id, }; Ok(Some(res)) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index eabc39b3e09cd..3f365c05942f0 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -768,20 +768,25 @@ pub(crate) fn handle_runnables( let config = snap.config.runnables(); match cargo_spec { Some(spec) => { + let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?; for cmd in ["check", "test"] { + let mut cargo_args = + vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()]; + if all_targets { + cargo_args.push("--all-targets".to_owned()); + } res.push(lsp_ext::Runnable { - label: format!("cargo {cmd} -p {} --all-targets", spec.package), + label: format!( + "cargo {cmd} -p {}{all_targets}", + spec.package, + all_targets = if all_targets { " --all-targets" } else { "" } + ), location: None, kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { workspace_root: Some(spec.workspace_root.clone().into()), override_cargo: config.override_cargo.clone(), - cargo_args: vec![ - cmd.to_string(), - "--package".to_string(), - spec.package.clone(), - "--all-targets".to_string(), - ], + cargo_args, cargo_extra_args: config.cargo_extra_args.clone(), executable_args: Vec::new(), expect_test: None, From 8bc826dd53a7a88798531d7c79d74dbf4d809e56 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 28 May 2023 13:30:34 +0200 Subject: [PATCH 537/806] Add diagnostic for `_` expressions (typed holes) --- crates/base-db/src/fixture.rs | 7 + crates/hir-def/src/resolver.rs | 6 + crates/hir-ty/src/builder.rs | 13 + crates/hir-ty/src/infer.rs | 45 ++-- crates/hir-ty/src/infer/expr.rs | 25 +- crates/hir/src/diagnostics.rs | 7 + crates/hir/src/lib.rs | 92 ++++--- .../src/handlers/typed_hole.rs | 232 ++++++++++++++++++ crates/ide-diagnostics/src/lib.rs | 2 + 9 files changed, 368 insertions(+), 61 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/typed_hole.rs diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index e89d3ae4bb339..5b11343173b30 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -21,6 +21,7 @@ use crate::{ pub const WORKSPACE: SourceRootId = SourceRootId(0); pub trait WithFixture: Default + SourceDatabaseExt + 'static { + #[track_caller] fn with_single_file(ra_fixture: &str) -> (Self, FileId) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -29,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { (db, fixture.files[0]) } + #[track_caller] fn with_many_files(ra_fixture: &str) -> (Self, Vec) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -37,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { (db, fixture.files) } + #[track_caller] fn with_files(ra_fixture: &str) -> Self { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -45,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { db } + #[track_caller] fn with_files_extra_proc_macros( ra_fixture: &str, proc_macros: Vec<(String, ProcMacro)>, @@ -56,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { db } + #[track_caller] fn with_position(ra_fixture: &str) -> (Self, FilePosition) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let offset = range_or_offset.expect_offset(); (db, FilePosition { file_id, offset }) } + #[track_caller] fn with_range(ra_fixture: &str) -> (Self, FileRange) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let range = range_or_offset.expect_range(); (db, FileRange { file_id, range }) } + #[track_caller] fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index afa3b33cc9fea..3eaff61b154a0 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -1046,6 +1046,12 @@ impl HasResolver for GenericDefId { } } +impl HasResolver for EnumVariantId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver { + self.parent.resolver(db) + } +} + impl HasResolver for VariantId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index 2362b08f79a2a..eec57ba3f80f1 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs @@ -194,6 +194,19 @@ impl TyBuilder<()> { params.placeholder_subst(db) } + pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into) -> Substitution { + let params = generics(db.upcast(), def.into()); + Substitution::from_iter( + Interner, + params.iter_id().map(|id| match id { + either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), + either::Either::Right(id) => { + unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) + } + }), + ) + } + pub fn subst_for_def( db: &dyn HirDatabase, def: impl Into, diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index f01ee1b4e600b..11c0ccf547d51 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -215,6 +215,10 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: Ty, }, + TypedHole { + expr: ExprId, + expected: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -600,29 +604,30 @@ impl<'a> InferenceContext<'a> { mismatch.actual = table.resolve_completely(mismatch.actual.clone()); } result.diagnostics.retain_mut(|diagnostic| { - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } - | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } - | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic - { - *ty = table.resolve_completely(ty.clone()); - // FIXME: Remove this when we are on par with rustc in terms of inference - if ty.contains_unknown() { - return false; - } + use InferenceDiagnostic::*; + match diagnostic { + ExpectedFunction { found: ty, .. } + | UnresolvedField { receiver: ty, .. } + | UnresolvedMethodCall { receiver: ty, .. } => { + *ty = table.resolve_completely(ty.clone()); + // FIXME: Remove this when we are on par with rustc in terms of inference + if ty.contains_unknown() { + return false; + } - if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } = - diagnostic - { - let clear = if let Some(ty) = field_with_same_name { - *ty = table.resolve_completely(ty.clone()); - ty.contains_unknown() - } else { - false - }; - if clear { - *field_with_same_name = None; + if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic { + if let Some(ty) = field_with_same_name { + *ty = table.resolve_completely(ty.clone()); + if ty.contains_unknown() { + *field_with_same_name = None; + } + } } } + TypedHole { expected: ty, .. } => { + *ty = table.resolve_completely(ty.clone()); + } + _ => (), } true }); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index f30758484300f..d7c6691ea0c7a 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -874,9 +874,15 @@ impl<'a> InferenceContext<'a> { }, Expr::Underscore => { // Underscore expressions may only appear in assignee expressions, - // which are handled by `infer_assignee_expr()`, so any underscore - // expression reaching this branch is an error. - self.err_ty() + // which are handled by `infer_assignee_expr()`. + // Any other underscore expression is an error, we render a specialized diagnostic + // to let the user know what type is expected though. + let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty()); + self.push_diagnostic(InferenceDiagnostic::TypedHole { + expr: tgt_expr, + expected: expected.clone(), + }); + expected } }; // use a new type variable if we got unknown here @@ -1001,12 +1007,13 @@ impl<'a> InferenceContext<'a> { } &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone())); - self.infer_expr( - repeat, - &Expectation::HasType( - TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), - ), - ); + let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner); + match self.body[repeat] { + Expr::Underscore => { + self.write_expr_ty(repeat, usize); + } + _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)), + } ( elem_ty, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 10893b62bfb57..b64d81490bb11 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -52,6 +52,7 @@ diagnostics![ PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, + TypedHole, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, @@ -73,6 +74,12 @@ pub struct BreakOutsideOfLoop { pub bad_value_break: bool, } +#[derive(Debug)] +pub struct TypedHole { + pub expr: InFile>, + pub expected: Type, +} + #[derive(Debug)] pub struct UnresolvedModule { pub decl: InFile>, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b1583c9d00b5e..5392cb6a32a17 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -69,7 +69,8 @@ use hir_ty::{ traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, - TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, + WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; @@ -91,10 +92,10 @@ pub use crate::{ IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, - UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, - UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, - UnusedMut, + ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, + UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, + UnresolvedProcMacro, UnusedMut, }, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, @@ -1005,6 +1006,10 @@ impl Struct { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn repr(self, db: &dyn HirDatabase) -> Option { db.struct_data(self.id).repr } @@ -1042,6 +1047,10 @@ impl Union { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn fields(self, db: &dyn HirDatabase) -> Vec { db.union_data(self.id) .variant_data @@ -1173,6 +1182,10 @@ impl Variant { self.parent } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id }) + } + pub fn name(self, db: &dyn HirDatabase) -> Name { db.enum_data(self.parent.id).variants[self.id].name.clone() } @@ -1574,6 +1587,16 @@ impl DefWithBody { let expr = expr_syntax(expr); acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) } + hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => { + let expr = expr_syntax(*expr); + acc.push( + TypedHole { + expr, + expected: Type::new(db, DefWithBodyId::from(self), expected.clone()), + } + .into(), + ) + } } } for (pat_or_expr, mismatch) in infer.type_mismatches() { @@ -1806,6 +1829,10 @@ impl Function { db.function_data(self.id).name.clone() } + pub fn ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type { let resolver = self.id.resolver(db.upcast()); @@ -2085,11 +2112,7 @@ impl Const { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.const_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } pub fn render_eval(self, db: &dyn HirDatabase) -> Result { @@ -2136,11 +2159,7 @@ impl Static { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.static_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } } @@ -3409,24 +3428,33 @@ impl Type { Type { env: environment, ty } } - fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into) -> Type { - let ty_def = def.into(); - let parent_subst = match ty_def { - TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container { - ItemContainerId::TraitId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - ItemContainerId::ImplId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - _ => None, + fn from_def(db: &dyn HirDatabase, def: impl Into + HasResolver) -> Type { + let ty = db.ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + TyDefId::AdtId(it) => GenericDefId::AdtId(it), + TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), + TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()), }, - _ => None, - }; - let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build(); - Type::new(db, def, ty) + ); + Type::new(db, def, ty.substitute(Interner, &substs)) + } + + fn from_value_def(db: &dyn HirDatabase, def: impl Into + HasResolver) -> Type { + let ty = db.value_ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), + ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), + ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), + ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)), + ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it), + ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()), + }, + ); + Type::new(db, def, ty.substitute(Interner, &substs)) } pub fn new_slice(ty: Type) -> Type { diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs new file mode 100644 index 0000000000000..e12bbcf682068 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -0,0 +1,232 @@ +use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, StructKind}; +use ide_db::{ + assists::{Assist, AssistId, AssistKind, GroupLabel}, + label::Label, + source_change::SourceChange, +}; +use syntax::AstNode; +use text_edit::TextEdit; + +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: typed-hole +// +// This diagnostic is triggered when an underscore expression is used in an invalid position. +pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())); + let (message, fixes) = if d.expected.is_unknown() { + ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None) + } else { + ( + format!( + "invalid `_` expression, expected type `{}`", + d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), + ), + fixes(ctx, d), + ) + }; + + Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option> { + let db = ctx.sema.db; + let root = db.parse_or_expand(d.expr.file_id); + let original_range = + d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?; + let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?; + let mut assists = vec![]; + scope.process_all_names(&mut |name, def| { + let ty = match def { + hir::ScopeDef::ModuleDef(it) => match it { + hir::ModuleDef::Function(it) => it.ty(db), + hir::ModuleDef::Adt(hir::Adt::Struct(it)) if it.kind(db) != StructKind::Record => { + it.constructor_ty(db) + } + hir::ModuleDef::Variant(it) if it.kind(db) != StructKind::Record => { + it.constructor_ty(db) + } + hir::ModuleDef::Const(it) => it.ty(db), + hir::ModuleDef::Static(it) => it.ty(db), + _ => return, + }, + hir::ScopeDef::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db), + hir::ScopeDef::Local(it) => it.ty(db), + _ => return, + }; + // FIXME: should also check coercions if it is at a coercion site + if !ty.contains_unknown() && ty.could_unify_with(db, &d.expected) { + assists.push(Assist { + id: AssistId("typed-hole", AssistKind::QuickFix), + label: Label::new(format!("Replace `_` with `{}`", name.display(db))), + group: Some(GroupLabel("Replace `_` with a matching entity in scope".to_owned())), + target: original_range.range, + source_change: Some(SourceChange::from_text_edit( + original_range.file_id, + TextEdit::replace(original_range.range, name.display(db).to_string()), + )), + trigger_signature_help: false, + }); + } + }); + if assists.is_empty() { + None + } else { + Some(assists) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fixes}; + + #[test] + fn unknown() { + check_diagnostics( + r#" +fn main() { + _; + //^ error: `_` expressions may only appear on the left-hand side of an assignment +} +"#, + ); + } + + #[test] + fn concrete_expectation() { + check_diagnostics( + r#" +fn main() { + if _ {} + //^ error: invalid `_` expression, expected type `bool` + let _: fn() -> i32 = _; + //^ error: invalid `_` expression, expected type `fn() -> i32` + let _: fn() -> () = _; // FIXME: This should trigger an assist because `main` matches via *coercion* + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + ); + } + + #[test] + fn integer_ty_var() { + check_diagnostics( + r#" +fn main() { + let mut x = 3; + x = _; + //^ 💡 error: invalid `_` expression, expected type `i32` +} +"#, + ); + } + + #[test] + fn ty_var_resolved() { + check_diagnostics( + r#" +fn main() { + let mut x = t(); + x = _; + //^ 💡 error: invalid `_` expression, expected type `&str` + x = ""; +} +fn t() -> T { loop {} } +"#, + ); + } + + #[test] + fn valid_positions() { + check_diagnostics( + r#" +fn main() { + let x = [(); _]; + let y: [(); 10] = [(); _]; + _ = 0; + (_,) = (1,); +} +"#, + ); + } + + #[test] + fn check_quick_fix() { + check_fixes( + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = _$0; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + vec![ + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = local; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = param; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = CP; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = Bar; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = C; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + ], + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 048dedf6bd15e..55a4a482d3b28 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -44,6 +44,7 @@ mod handlers { pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; pub(crate) mod unresolved_extern_crate; @@ -290,6 +291,7 @@ pub fn diagnostics( AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), + AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), From cfcb7fc859ba4fd707d86e1b78748d6e3acbf26d Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Fri, 26 May 2023 11:19:35 -0600 Subject: [PATCH 538/806] Replace EarlyBinder(x) with EarlyBinder::new(x) --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 4 ++-- clippy_utils/src/consts.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b27ffe73ffda4..a418a910ba8ea 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1219,7 +1219,7 @@ fn needless_borrow_impl_arg_position<'tcx>( return false; } - let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); + let predicate = EarlyBinder::new(predicate).subst(cx.tcx, &substs_with_referent_ty); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(); infcx.predicate_must_hold_modulo_regions(&obligation) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index b2071f4dcb1e2..af2aac6ac0d0a 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -243,7 +243,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs | ty::Ref(..) | ty::Slice(_) | ty::Tuple(_) => { - format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs)) + format!("<{}>", EarlyBinder::new(ty).subst(cx.tcx, substs)) }, _ => ty.to_string(), } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 6841aaf626ca5..d4cc14bb85632 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -241,7 +241,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) { - item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + item_ty == EarlyBinder::new(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) } else { false } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 67b7d3691dc02..fdacfa49e92d5 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -428,7 +428,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< })); if trait_predicates.any(|predicate| { - let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); + let predicate = EarlyBinder::new(predicate).subst(cx.tcx, new_subst); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { @@ -438,7 +438,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let output_ty = fn_sig.output(); if output_ty.contains(*param_ty) { if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions( - new_subst, cx.param_env, EarlyBinder(output_ty)) { + new_subst, cx.param_env, EarlyBinder::new(output_ty)) { expr = parent_expr; ty = new_ty; continue; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fb772644c0d64..843538e1eb2db 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -462,7 +462,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let substs = if self.substs.is_empty() { substs } else { - EarlyBinder(substs).subst(self.lcx.tcx, self.substs) + EarlyBinder::new(substs).subst(self.lcx.tcx, self.substs) }; let result = self From a37852e54b5617ef72fcfd425664d9e8b19aa9af Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Fri, 26 May 2023 12:14:48 -0600 Subject: [PATCH 539/806] Make EarlyBinder's inner value private; and fix all of the resulting errors --- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2abdfacd27634..e6fd65f001a6e 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -138,7 +138,7 @@ fn collect_unsafe_exprs<'tcx>( .type_dependent_def_id(expr.hir_id) .map(|def_id| cx.tcx.fn_sig(def_id)) { - if sig.0.unsafety() == Unsafety::Unsafe { + if sig.skip_binder().unsafety() == Unsafety::Unsafe { unsafe_ops.push(("unsafe method call occurs here", expr.span)); } } From 51368793b462306724eafa7d9fb0797329b671f3 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 26 May 2023 00:45:37 +0330 Subject: [PATCH 540/806] MIR episode 6 --- crates/hir-def/src/body/lower.rs | 157 ++++++-- crates/hir-def/src/body/pretty.rs | 30 +- crates/hir-def/src/body/scope.rs | 6 - crates/hir-def/src/hir.rs | 19 +- crates/hir-ty/src/consteval.rs | 42 +- crates/hir-ty/src/consteval/tests.rs | 75 +++- .../hir-ty/src/consteval/tests/intrinsics.rs | 24 ++ crates/hir-ty/src/db.rs | 23 +- crates/hir-ty/src/display.rs | 16 +- crates/hir-ty/src/infer.rs | 16 - crates/hir-ty/src/infer/closure.rs | 26 +- crates/hir-ty/src/infer/expr.rs | 18 - crates/hir-ty/src/infer/mutability.rs | 3 +- crates/hir-ty/src/infer/pat.rs | 6 +- crates/hir-ty/src/layout.rs | 40 +- crates/hir-ty/src/layout/adt.rs | 24 +- crates/hir-ty/src/layout/tests.rs | 11 +- crates/hir-ty/src/method_resolution.rs | 4 +- crates/hir-ty/src/mir.rs | 12 +- crates/hir-ty/src/mir/eval.rs | 318 ++++----------- crates/hir-ty/src/mir/eval/shim.rs | 42 +- crates/hir-ty/src/mir/eval/tests.rs | 253 +++++++++++- crates/hir-ty/src/mir/lower.rs | 203 ++++++---- .../hir-ty/src/mir/lower/pattern_matching.rs | 170 +++++--- crates/hir-ty/src/mir/monomorphization.rs | 369 ++++++++++++++++++ crates/hir-ty/src/mir/pretty.rs | 2 +- crates/hir-ty/src/tests/macros.rs | 24 +- crates/hir-ty/src/tests/never_type.rs | 28 ++ crates/hir-ty/src/tests/patterns.rs | 10 + crates/hir-ty/src/tests/regression.rs | 22 +- crates/hir/src/lib.rs | 27 +- .../src/handlers/extract_function.rs | 3 + .../src/completions/lifetime.rs | 1 + .../src/handlers/break_outside_of_loop.rs | 4 +- crates/ide/src/syntax_highlighting/tests.rs | 2 +- 35 files changed, 1474 insertions(+), 556 deletions(-) create mode 100644 crates/hir-ty/src/mir/monomorphization.rs diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index e9cb51d5bf8d2..acc9943481a8c 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -31,8 +31,8 @@ use crate::{ expander::Expander, hir::{ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr, - ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, - RecordLitField, Statement, + ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, Pat, PatId, + RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -295,13 +295,7 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) } - ast::Expr::ForExpr(e) => { - let label = e.label().map(|label| self.collect_label(label)); - let iterable = self.collect_expr_opt(e.iterable()); - let pat = self.collect_pat_top(e.pat()); - let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) - } + ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), ast::Expr::CallExpr(e) => { let is_rustc_box = { let attrs = e.attrs(); @@ -703,6 +697,91 @@ impl ExprCollector<'_> { expr_id } + /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: + /// ```ignore (pseudo-rust) + /// match IntoIterator::into_iter() { + /// mut iter => { + /// [opt_ident]: loop { + /// match Iterator::next(&mut iter) { + /// None => break, + /// Some() => , + /// }; + /// } + /// } + /// } + /// ``` + fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { + let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: { + if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) { + if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) { + if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) { + if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) { + break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none); + } + } + } + } + // Some of the needed lang items are missing, so we can't desugar + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let head = self.collect_expr_opt(e.iterable()); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone()); + let iterator = self.alloc_expr( + Expr::Call { + callee: into_iter_fn_expr, + args: Box::new([head]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let none_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))), + guard: None, + expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()), + }; + let some_pat = Pat::TupleStruct { + path: Some(Box::new(option_some)), + args: Box::new([self.collect_pat_top(e.pat())]), + ellipsis: None, + }; + let some_arm = MatchArm { + pat: self.alloc_pat_desugared(some_pat), + guard: None, + expr: self.collect_expr_opt(e.loop_body().map(|x| x.into())), + }; + let iter_name = Name::generate_new_name(); + let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable); + let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone()); + let iter_expr_mut = self.alloc_expr( + Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, + syntax_ptr.clone(), + ); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone()); + let iter_next_expr = self.alloc_expr( + Expr::Call { + callee: iter_next_fn_expr, + args: Box::new([iter_expr_mut]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let loop_inner = self.alloc_expr( + Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, + syntax_ptr.clone(), + ); + let label = e.label().map(|label| self.collect_label(label)); + let loop_outer = + self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone()); + let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); + self.alloc_expr( + Expr::Match { + expr: iterator, + arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]), + }, + syntax_ptr.clone(), + ) + } + /// Desugar `ast::TryExpr` from: `?` into: /// ```ignore (pseudo-rust) /// match Try::branch() { @@ -1159,22 +1238,12 @@ impl ExprCollector<'_> { } #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676 ast::Pat::LiteralPat(lit) => 'b: { - if let Some(ast_lit) = lit.literal() { - let mut hir_lit: Literal = ast_lit.kind().into(); - if lit.minus_token().is_some() { - let Some(h) = hir_lit.negate() else { - break 'b Pat::Missing; - }; - hir_lit = h; - } - let expr = Expr::Literal(hir_lit); - let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); - let expr_id = self.alloc_expr(expr, expr_ptr); - Pat::Lit(expr_id) - } else { - Pat::Missing - } - }, + let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing }; + let expr = Expr::Literal(hir_lit); + let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); + let expr_id = self.alloc_expr(expr, expr_ptr); + Pat::Lit(expr_id) + } ast::Pat::RestPat(_) => { // `RestPat` requires special handling and should not be mapped // to a Pat. Here we are using `Pat::Missing` as a fallback for @@ -1215,8 +1284,30 @@ impl ExprCollector<'_> { } None => Pat::Missing, }, - // FIXME: implement - ast::Pat::RangePat(_) => Pat::Missing, + // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference. + ast::Pat::RangePat(p) => { + let mut range_part_lower = |p: Option| { + p.and_then(|x| match &x { + ast::Pat::LiteralPat(x) => { + Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0))) + } + ast::Pat::IdentPat(p) => { + let name = + p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + Some(Box::new(LiteralOrConst::Const(name.into()))) + } + ast::Pat::PathPat(p) => p + .path() + .and_then(|path| self.expander.parse_path(self.db, path)) + .map(LiteralOrConst::Const) + .map(Box::new), + _ => None, + }) + }; + let start = range_part_lower(p.start()); + let end = range_part_lower(p.end()); + Pat::Range { start, end } + } }; let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, Either::Left(ptr)) @@ -1338,6 +1429,18 @@ impl ExprCollector<'_> { // endregion: labels } +fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { + let ast_lit = lit.literal()?; + let mut hir_lit: Literal = ast_lit.kind().into(); + if lit.minus_token().is_some() { + let Some(h) = hir_lit.negate() else { + return None; + }; + hir_lit = h; + } + Some((hir_lit, ast_lit)) +} + impl ExprCollector<'_> { fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.to_source(ptr); diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 7390a8f1f2b18..88380aa355d16 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -7,7 +7,8 @@ use syntax::ast::HasName; use crate::{ hir::{ - Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement, + Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst, + Movability, Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, type_ref::TypeRef, @@ -184,16 +185,6 @@ impl<'a> Printer<'a> { self.print_expr(*condition); self.print_expr(*body); } - Expr::For { iterable, pat, body, label } => { - if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name.display(self.db)); - } - w!(self, "for "); - self.print_pat(*pat); - w!(self, " in "); - self.print_expr(*iterable); - self.print_expr(*body); - } Expr::Call { callee, args, is_assignee_expr: _ } => { self.print_expr(*callee); w!(self, "("); @@ -534,9 +525,13 @@ impl<'a> Printer<'a> { w!(self, "}}"); } Pat::Range { start, end } => { - self.print_expr(*start); - w!(self, "..."); - self.print_expr(*end); + if let Some(start) = start { + self.print_literal_or_const(start); + } + w!(self, "..="); + if let Some(end) = end { + self.print_literal_or_const(end); + } } Pat::Slice { prefix, slice, suffix } => { w!(self, "["); @@ -627,6 +622,13 @@ impl<'a> Printer<'a> { } } + fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) { + match literal_or_const { + LiteralOrConst::Literal(l) => self.print_literal(l), + LiteralOrConst::Const(c) => self.print_path(c), + } + } + fn print_literal(&mut self, literal: &Literal) { match literal { Literal::String(it) => w!(self, "{:?}", it), diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index b81130c0c2493..69741c445fbae 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -228,12 +228,6 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope scopes.set_scope(expr, scope); compute_block_scopes(statements, *tail, body, scopes, &mut scope); } - Expr::For { iterable, pat, body: body_expr, label } => { - compute_expr_scopes(*iterable, body, scopes, scope); - let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); - scopes.add_pat_bindings(body, scope, *pat); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); - } Expr::While { condition, body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); compute_expr_scopes(*condition, body, scopes, &mut scope); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index a42f8183abcad..8102efdba3089 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -96,6 +96,13 @@ pub enum Literal { Float(FloatTypeWrapper, Option), } +#[derive(Debug, Clone, Eq, PartialEq)] +/// Used in range patterns. +pub enum LiteralOrConst { + Literal(Literal), + Const(Path), +} + impl Literal { pub fn negate(self) -> Option { if let Literal::Int(i, k) = self { @@ -189,12 +196,6 @@ pub enum Expr { body: ExprId, label: Option, }, - For { - iterable: ExprId, - pat: PatId, - body: ExprId, - label: Option, - }, Call { callee: ExprId, args: Box<[ExprId]>, @@ -382,10 +383,6 @@ impl Expr { f(*condition); f(*body); } - Expr::For { iterable, body, .. } => { - f(*iterable); - f(*body); - } Expr::Call { callee, args, .. } => { f(*callee); args.iter().copied().for_each(f); @@ -526,7 +523,7 @@ pub enum Pat { Tuple { args: Box<[PatId]>, ellipsis: Option }, Or(Box<[PatId]>), Record { path: Option>, args: Box<[RecordFieldPat]>, ellipsis: bool }, - Range { start: ExprId, end: ExprId }, + Range { start: Option>, end: Option> }, Slice { prefix: Box<[PatId]>, slice: Option, suffix: Box<[PatId]> }, Path(Box), Lit(ExprId), diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 0df2b39bbdc91..40b63b17b5aa4 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -14,9 +14,9 @@ use stdx::never; use triomphe::Arc; use crate::{ - db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, - to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg, - Interner, MemoryMap, Substitution, Ty, TyBuilder, + db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, + mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData, + ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder, }; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; @@ -130,14 +130,15 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const { /// Interns a constant scalar with the given type pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const { + let layout = db.layout_of_ty(ty.clone(), krate); let bytes = match value { ConstRef::Int(i) => { // FIXME: We should handle failure of layout better. - let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } ConstRef::UInt(i) => { - let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()), @@ -206,15 +207,22 @@ pub(crate) fn const_eval_query( subst: Substitution, ) -> Result { let body = match def { - GeneralConstId::ConstId(c) => db.mir_body(c.into())?, + GeneralConstId::ConstId(c) => { + db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))? + } GeneralConstId::AnonymousConstId(c) => { let (def, root) = db.lookup_intern_anonymous_const(c); let body = db.body(def); let infer = db.infer(def); - Arc::new(lower_to_mir(db, def, &body, &infer, root)?) + Arc::new(monomorphize_mir_body_bad( + db, + lower_to_mir(db, def, &body, &infer, root)?, + subst, + db.trait_environment_for_body(def), + )?) } }; - let c = interpret_mir(db, &body, subst, false).0?; + let c = interpret_mir(db, &body, false).0?; Ok(c) } @@ -222,8 +230,12 @@ pub(crate) fn const_eval_static_query( db: &dyn HirDatabase, def: StaticId, ) -> Result { - let body = db.mir_body(def.into())?; - let c = interpret_mir(db, &body, Substitution::empty(Interner), false).0?; + let body = db.monomorphized_mir_body( + def.into(), + Substitution::empty(Interner), + db.trait_environment_for_body(def.into()), + )?; + let c = interpret_mir(db, &body, false).0?; Ok(c) } @@ -245,8 +257,12 @@ pub(crate) fn const_eval_discriminant_variant( }; return Ok(value); } - let mir_body = db.mir_body(def)?; - let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false).0?; + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, &mir_body, false).0?; let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -271,7 +287,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true).0 { + if let Ok(result) = interpret_mir(db, &mir_body, true).0 { return result; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 6ff8e2de67de5..6a7d045648635 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -156,11 +156,23 @@ fn casts() { check_number( r#" //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = &[10, 20, 30, 40] as &[i32]; + a.len() + }; + "#, + 4, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice const GOAL: usize = { let a = [10, 20, 3, 15]; let x: &[i32] = &a; let y: *const [i32] = x; let z = y as *const [u8]; // slice fat pointer cast don't touch metadata + let q = z as *const str; + let p = q as *const [u8]; let w = unsafe { &*z }; w.len() }; @@ -987,11 +999,15 @@ fn path_pattern_matching() { const MY_SEASON: Season = Summer; + impl Season { + const FALL: Season = Fall; + } + const fn f(x: Season) -> i32 { match x { Spring => 1, MY_SEASON => 2, - Fall => 3, + Season::FALL => 3, Winter => 4, } } @@ -1031,6 +1047,27 @@ fn pattern_matching_literal() { ); } +#[test] +fn pattern_matching_range() { + check_number( + r#" + pub const L: i32 = 6; + mod x { + pub const R: i32 = 100; + } + const fn f(x: i32) -> i32 { + match x { + -1..=5 => x * 10, + L..=x::R => x * 100, + _ => x, + } + } + const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000); + "#, + 11008, + ); +} + #[test] fn pattern_matching_slice() { check_number( @@ -1045,6 +1082,22 @@ fn pattern_matching_slice() { "#, 10 + 4 + 60 + 16, ); + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(x: &[usize]) -> usize { + match x { + [] => 0, + [a] => *a, + &[a, b] => a + b, + [a, b @ .., c, d] => *a + b.len() + *c + *d, + } + } + const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100]) + + f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]); + "#, + 33213, + ); } #[test] @@ -2105,6 +2158,26 @@ fn const_generic_subst_fn() { "#, 11, ); + check_number( + r#" + fn f(x: [i32; N]) -> usize { + N + } + + trait ArrayExt { + fn f(self) -> usize; + } + + impl ArrayExt for [T; N] { + fn g(self) -> usize { + f(self) + } + } + + const GOAL: usize = f([1, 2, 5]); + "#, + 3, + ); } #[test] diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 0dd120e5b9ea1..1feb9a4441c03 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -67,6 +67,30 @@ fn wrapping_add() { ); } +#[test] +fn saturating_add() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn saturating_add(a: T, b: T) -> T; + } + + const GOAL: u8 = saturating_add(10, 250); + "#, + 255, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn saturating_add(a: T, b: T) -> T; + } + + const GOAL: i8 = saturating_add(5, 8); + "#, + 13, + ); +} + #[test] fn allocator() { check_number( diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 3aa43dedcd571..ca8a394e360dc 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -41,6 +41,23 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::mir::mir_body_for_closure_query)] fn mir_body_for_closure(&self, def: ClosureId) -> Result, MirLowerError>; + #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] + #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)] + fn monomorphized_mir_body( + &self, + def: DefWithBodyId, + subst: Substitution, + env: Arc, + ) -> Result, MirLowerError>; + + #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] + fn monomorphized_mir_body_for_closure( + &self, + def: ClosureId, + subst: Substitution, + env: Arc, + ) -> Result, MirLowerError>; + #[salsa::invoke(crate::mir::borrowck_query)] fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; @@ -84,7 +101,11 @@ pub trait HirDatabase: DefDatabase + Upcast { def: AdtId, subst: Substitution, krate: CrateId, - ) -> Result; + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::layout::layout_of_ty_query)] + #[salsa::cycle(crate::layout::layout_of_ty_recover)] + fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 3a34485adc44d..058d5059b1692 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -26,9 +26,7 @@ use stdx::never; use crate::{ db::HirDatabase, - from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, - layout::layout_of_ty, - lt_from_placeholder_idx, + from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, mapping::from_chalk, mir::pad16, primitive, to_assoc_type_id, @@ -309,6 +307,8 @@ pub enum ClosureStyle { RANotation, /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage. ClosureWithId, + /// `{closure#14825}`, useful for internal usage. + ClosureWithSubst, /// `…`, which is the `TYPE_HINT_TRUNCATION` Hide, } @@ -507,7 +507,7 @@ fn render_const_scalar( _ => f.write_str(""), }, chalk_ir::TyKind::Tuple(_, subst) => { - let Ok(layout) = layout_of_ty(f.db, ty, krate) else { + let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else { return f.write_str(""); }; f.write_str("(")?; @@ -520,7 +520,7 @@ fn render_const_scalar( } let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = layout_of_ty(f.db, &ty, krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { f.write_str("")?; continue; }; @@ -545,7 +545,7 @@ fn render_const_scalar( .offset(u32::from(id.into_raw()) as usize) .bytes_usize(); let ty = field_types[id].clone().substitute(Interner, subst); - let Ok(layout) = layout_of_ty(f.db, &ty, krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -931,6 +931,10 @@ impl HirDisplay for Ty { ClosureStyle::ClosureWithId => { return write!(f, "{{closure#{:?}}}", id.0.as_u32()) } + ClosureStyle::ClosureWithSubst => { + write!(f, "{{closure#{:?}}}", id.0.as_u32())?; + return hir_fmt_generics(f, substs, None); + } _ => (), } let sig = ClosureSubst(substs).sig_ty().callable_sig(db); diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 11c0ccf547d51..fa81fe39aa116 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1153,22 +1153,6 @@ impl<'a> InferenceContext<'a> { self.db.lang_item(krate, item) } - fn resolve_into_iter_item(&self) -> Option { - let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)? - .as_function()? - .lookup(self.db.upcast()).container - else { return None }; - self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter]) - } - - fn resolve_iterator_item(&self) -> Option { - let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)? - .as_function()? - .lookup(self.db.upcast()).container - else { return None }; - self.db.trait_data(trait_).associated_type_by_name(&name![Item]) - } - fn resolve_output_on(&self, trait_: TraitId) -> Option { self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 787c5c54a2916..662f2e5fa161d 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -5,7 +5,7 @@ use std::{cmp, collections::HashMap, convert::Infallible, mem}; use chalk_ir::{ cast::Cast, fold::{FallibleTypeFolder, TypeFoldable}, - AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, + AliasEq, AliasTy, BoundVar, ConstData, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, }; use hir_def::{ data::adt::VariantData, @@ -29,8 +29,8 @@ use crate::{ static_lifetime, to_chalk_trait_id, traits::FnTrait, utils::{self, generics, pattern_matching_dereference_count, Generics}, - Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, Interner, - Substitution, Ty, TyExt, + Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, ConstValue, DynTy, FnPointer, FnSig, + Interner, Substitution, Ty, TyExt, }; use super::{Expectation, InferenceContext}; @@ -259,6 +259,23 @@ impl CapturedItemWithoutTy { Interner } + fn try_fold_free_placeholder_const( + &mut self, + ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.param_idx(x) else { + return Err(()); + }; + Ok(ConstData { + ty, + value: ConstValue::BoundVar(BoundVar::new(outer_binder, idx)), + } + .intern(Interner)) + } + fn try_fold_free_placeholder_ty( &mut self, idx: chalk_ir::PlaceholderIndex, @@ -490,8 +507,7 @@ impl InferenceContext<'_> { self.consume_expr(*tail); } } - Expr::While { condition, body, label: _ } - | Expr::For { iterable: condition, pat: _, body, label: _ } => { + Expr::While { condition, body, label: _ } => { self.consume_expr(*condition); self.consume_expr(*body); } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d7c6691ea0c7a..33e98ac86cf6b 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -211,24 +211,6 @@ impl<'a> InferenceContext<'a> { self.diverges = Diverges::Maybe; TyBuilder::unit() } - &Expr::For { iterable, body, pat, label } => { - let iterable_ty = self.infer_expr(iterable, &Expectation::none()); - let into_iter_ty = - self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); - let pat_ty = self - .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item()); - - self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty); - - self.infer_top_pat(pat, &pat_ty); - self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| { - this.infer_expr(body, &Expectation::HasType(TyBuilder::unit())); - }); - - // the body may not run, so it diverging doesn't mean we diverge - self.diverges = Diverges::Maybe; - TyBuilder::unit() - } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => { assert_eq!(args.len(), arg_types.len()); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 7794bd5c39f92..447834243908a 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -69,8 +69,7 @@ impl<'a> InferenceContext<'a> { self.infer_mut_expr(*tail, Mutability::Not); } } - &Expr::For { iterable: c, pat: _, body, label: _ } - | &Expr::While { condition: c, body, label: _ } => { + &Expr::While { condition: c, body, label: _ } => { self.infer_mut_expr(c, Mutability::Not); self.infer_mut_expr(body, Mutability::Not); } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 0c2b179a10d69..2480f8babac4f 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -255,9 +255,9 @@ impl<'a> InferenceContext<'a> { self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm) } Pat::Wild => expected.clone(), - Pat::Range { start, end } => { - let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone())); - self.infer_expr(*end, &Expectation::has_type(start_ty)) + Pat::Range { .. } => { + // FIXME: do some checks here. + expected.clone() } &Pat::Lit(expr) => { // Don't emit type mismatches again, the expression lowering already did that. diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 693c0494dbd74..35d3407c16c13 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -77,14 +77,18 @@ impl<'a> LayoutCalculator for LayoutCx<'a> { } } -pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { +pub fn layout_of_ty_query( + db: &dyn HirDatabase, + ty: Ty, + krate: CrateId, +) -> Result, LayoutError> { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = &*cx.current_data_layout(); let trait_env = Arc::new(TraitEnvironment::empty(krate)); let ty = normalize(db, trait_env, ty.clone()); - let layout = match ty.kind(Interner) { - TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone(), krate)?, + let result = match ty.kind(Interner) { + TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate), TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( dl, @@ -141,9 +145,9 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result, _>>()?; - let fields = fields.iter().collect::>(); + let fields = fields.iter().map(|x| &**x).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } @@ -151,7 +155,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result Result { - let element = layout_of_ty(db, element, krate)?; + let element = db.layout_of_ty(element.clone(), krate)?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, @@ -206,7 +210,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { // pointee is sized - return Ok(Layout::scalar(dl, data_ptr)); + return Ok(Arc::new(Layout::scalar(dl, data_ptr))); } }; @@ -248,7 +252,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { let infer = db.infer(func.into()); - layout_of_ty(db, &infer.type_of_rpit[idx], krate)? + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) @@ -262,14 +266,13 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result, _>>()?; - let fields = fields.iter().collect::>(); + let fields = fields.iter().map(|x| &**x).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) .ok_or(LayoutError::Unknown)? @@ -284,7 +287,16 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result return Err(LayoutError::HasPlaceholder), }; - Ok(layout) + Ok(Arc::new(result)) +} + +pub fn layout_of_ty_recover( + _: &dyn HirDatabase, + _: &[String], + _: &Ty, + _: &CrateId, +) -> Result, LayoutError> { + user_error!("infinite sized recursive type"); } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index da609a0957348..bd2752a7119ab 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -10,6 +10,7 @@ use hir_def::{ }; use la_arena::RawIdx; use smallvec::SmallVec; +use triomphe::Arc; use crate::{ db::HirDatabase, @@ -18,7 +19,7 @@ use crate::{ Substitution, }; -use super::{layout_of_ty, LayoutCx}; +use super::LayoutCx; pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0))) @@ -29,14 +30,14 @@ pub fn layout_of_adt_query( def: AdtId, subst: Substitution, krate: CrateId, -) -> Result { +) -> Result, LayoutError> { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { var.fields() .iter() - .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate)) + .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate)) .collect::, _>>() }; let (variants, repr) = match def { @@ -67,11 +68,13 @@ pub fn layout_of_adt_query( (r, data.repr.unwrap_or_default()) } }; - let variants = - variants.iter().map(|x| x.iter().collect::>()).collect::>(); + let variants = variants + .iter() + .map(|x| x.iter().map(|x| &**x).collect::>()) + .collect::>(); let variants = variants.iter().map(|x| x.iter().collect()).collect(); - if matches!(def, AdtId::UnionId(..)) { - cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown) + let result = if matches!(def, AdtId::UnionId(..)) { + cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { cx.layout_of_struct_or_enum( &repr, @@ -103,8 +106,9 @@ pub fn layout_of_adt_query( .and_then(|x| x.last().map(|x| x.is_unsized())) .unwrap_or(true), ) - .ok_or(LayoutError::SizeOverflow) - } + .ok_or(LayoutError::SizeOverflow)? + }; + Ok(Arc::new(result)) } fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) { @@ -129,7 +133,7 @@ pub fn layout_of_adt_recover( _: &AdtId, _: &Substitution, _: &CrateId, -) -> Result { +) -> Result, LayoutError> { user_error!("infinite sized recursive type"); } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 1a90a467157c1..fca2e09ff0a99 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use base_db::fixture::WithFixture; use chalk_ir::{AdtId, TyKind}; use hir_def::db::DefDatabase; +use triomphe::Arc; use crate::{ db::HirDatabase, @@ -11,15 +12,13 @@ use crate::{ Interner, Substitution, }; -use super::layout_of_ty; - mod closure; fn current_machine_data_layout() -> String { project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap() } -fn eval_goal(ra_fixture: &str, minicore: &str) -> Result { +fn eval_goal(ra_fixture: &str, minicore: &str) -> Result, LayoutError> { let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}", @@ -47,11 +46,11 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result { }) .unwrap(); let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner); - layout_of_ty(&db, &goal_ty, module_id.krate()) + db.layout_of_ty(goal_ty, module_id.krate()) } /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait` -fn eval_expr(ra_fixture: &str, minicore: &str) -> Result { +fn eval_expr(ra_fixture: &str, minicore: &str) -> Result, LayoutError> { let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}", @@ -75,7 +74,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result { let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0; let infer = db.infer(adt_id.into()); let goal_ty = infer.type_of_binding[b].clone(); - layout_of_ty(&db, &goal_ty, module_id.krate()) + db.layout_of_ty(goal_ty, module_id.krate()) } #[track_caller] diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 7208bebb3bd1b..6fa3d1351a916 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -782,7 +782,9 @@ fn find_matching_impl( .into_iter() .map(|b| b.cast(Interner)); let goal = crate::Goal::all(Interner, wcs); - table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs))) + table.try_obligation(goal.clone())?; + table.register_obligation(goal); + Some((impl_data, table.resolve_completely(impl_substs))) }) }) } diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index f8451f28d732a..2345bab0bb4df 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -19,12 +19,17 @@ mod eval; mod lower; mod borrowck; mod pretty; +mod monomorphization; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap}; pub use lower::{ lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError, }; +pub use monomorphization::{ + monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, + monomorphized_mir_body_query, monomorphized_mir_body_recover, +}; use smallvec::{smallvec, SmallVec}; use stdx::{impl_from, never}; @@ -37,7 +42,7 @@ fn return_slot() -> LocalId { LocalId::from_raw(RawIdx::from(0)) } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Local { pub ty: Ty, } @@ -780,7 +785,6 @@ pub enum CastKind { FloatToInt, FloatToFloat, IntToFloat, - PtrToPtr, FnPtrToPtr, } @@ -952,7 +956,7 @@ pub struct Statement { pub span: MirSpan, } -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct BasicBlock { /// List of statements in this block. pub statements: Vec, @@ -974,7 +978,7 @@ pub struct BasicBlock { pub is_cleanup: bool, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { pub basic_blocks: Arena, pub locals: Arena, diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index a1f69b1f28667..28e7759db39a1 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -3,20 +3,17 @@ use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range}; use base_db::{CrateId, FileId}; -use chalk_ir::{ - fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, - DebruijnIndex, Mutability, ProjectionTy, -}; +use chalk_ir::Mutability; use either::Either; use hir_def::{ builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, lang_item::{lang_attr, LangItem}, layout::{TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, HasModule, ItemContainerId, - Lookup, StaticId, TypeOrConstParamId, VariantId, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, + VariantId, }; -use hir_expand::{name::Name, InFile}; +use hir_expand::InFile; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; @@ -27,14 +24,13 @@ use crate::{ consteval::{intern_const_scalar, try_const_usize, ConstEvalError}, db::HirDatabase, display::{ClosureStyle, HirDisplay}, - from_placeholder_idx, - infer::{normalize, PointerCast}, - layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx}, + infer::PointerCast, + layout::{Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, - method_resolution::{is_dyn_method, lookup_impl_const, lookup_impl_method}, + method_resolution::{is_dyn_method, lookup_impl_method}, name, static_lifetime, traits::FnTrait, - utils::{generics, ClosureSubst, Generics}, + utils::ClosureSubst, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; @@ -279,7 +275,6 @@ pub enum MirEvalError { /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. UndefinedBehavior(String), - GenericArgNotProvided(TypeOrConstParamId, Substitution), Panic(String), MirLowerError(FunctionId, MirLowerError), MirLowerErrorForClosure(ClosureId, MirLowerError), @@ -348,20 +343,6 @@ impl MirEvalError { ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() )?; } - MirEvalError::GenericArgNotProvided(id, subst) => { - let parent = id.parent; - let param = &db.generic_params(parent).type_or_consts[id.local_id]; - writeln!( - f, - "Generic arg not provided for {}", - param.name().unwrap_or(&Name::missing()).display(db.upcast()) - )?; - writeln!(f, "Provided args: [")?; - for g in subst.iter(Interner) { - write!(f, " {},", g.display(db).to_string())?; - } - writeln!(f, "]")?; - } MirEvalError::MirLowerError(func, err) => { let function_name = db.function_data(*func); writeln!( @@ -416,7 +397,6 @@ impl std::fmt::Debug for MirEvalError { Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."), Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"), Self::StackOverflow => write!(f, "stack overflow"), - Self::GenericArgNotProvided(..) => f.debug_tuple("GenericArgNotProvided").finish(), Self::MirLowerError(arg0, arg1) => { f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() } @@ -471,14 +451,12 @@ impl DropFlags { struct Locals<'a> { ptr: &'a ArenaMap, body: &'a MirBody, - subst: &'a Substitution, drop_flags: DropFlags, } pub fn interpret_mir( db: &dyn HirDatabase, body: &MirBody, - subst: Substitution, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -489,17 +467,11 @@ pub fn interpret_mir( let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); let x: Result = (|| { - let ty = evaluator.ty_filler(&ty, &subst, body.owner)?; - let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?; + let bytes = evaluator.interpret_mir(&body, None.into_iter())?; let mut memory_map = evaluator.create_memory_map( &bytes, &ty, - &Locals { - ptr: &ArenaMap::new(), - body: &body, - subst: &subst, - drop_flags: DropFlags::default(), - }, + &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }, )?; memory_map.vtable = evaluator.vtable_map.clone(); return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); @@ -565,8 +537,7 @@ impl Evaluator<'_> { locals: &'a Locals<'a>, ) -> Result<(Address, Ty, Option)> { let mut addr = locals.ptr[p.local].addr; - let mut ty: Ty = - self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; + let mut ty: Ty = locals.body.locals[p.local].ty.clone(); let mut metadata: Option = None; // locals are always sized for proj in &*p.projection { let prev_ty = ty.clone(); @@ -689,17 +660,13 @@ impl Evaluator<'_> { Ok((addr, ty, metadata)) } - fn layout(&self, ty: &Ty) -> Result { - layout_of_ty(self.db, ty, self.crate_id) + fn layout(&self, ty: &Ty) -> Result> { + self.db + .layout_of_ty(ty.clone(), self.crate_id) .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) } - fn layout_filled(&self, ty: &Ty, locals: &Locals<'_>) -> Result { - let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?; - self.layout(ty) - } - - fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result { + fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| { MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) }) @@ -735,7 +702,6 @@ impl Evaluator<'_> { &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; @@ -743,12 +709,8 @@ impl Evaluator<'_> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let mut locals = Locals { - ptr: &ArenaMap::new(), - body: &body, - subst: &subst, - drop_flags: DropFlags::default(), - }; + let mut locals = + Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }; let (locals_ptr, stack_size) = { let mut stack_ptr = self.stack.len(); let addr = body @@ -882,7 +844,17 @@ impl Evaluator<'_> { } Owned(r) } - Rvalue::Len(_) => not_supported!("rvalue len"), + Rvalue::Len(p) => { + let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; + match metadata { + Some(m) => m, + None => { + return Err(MirEvalError::TypeError( + "type without metadata is used for Rvalue::Len", + )); + } + } + } Rvalue::UnaryOp(op, val) => { let mut c = self.eval_operand(val, locals)?.get(&self)?; let mut ty = self.operand_ty(val, locals)?; @@ -1080,7 +1052,7 @@ impl Evaluator<'_> { } return Ok(Owned(0u128.to_le_bytes().to_vec())); }; - match layout.variants { + match &layout.variants { Variants::Single { index } => { let r = self.const_eval_discriminant(EnumVariantId { parent: enum_id, @@ -1102,14 +1074,14 @@ impl Evaluator<'_> { TagEncoding::Niche { untagged_variant, niche_start, .. } => { let tag = &bytes[offset..offset + size]; let candidate_tag = i128::from_le_bytes(pad16(tag, false)) - .wrapping_sub(niche_start as i128) + .wrapping_sub(*niche_start as i128) as usize; let variant = variants .iter_enumerated() .map(|(x, _)| x) - .filter(|x| *x != untagged_variant) + .filter(|x| x != untagged_variant) .nth(candidate_tag) - .unwrap_or(untagged_variant) + .unwrap_or(*untagged_variant) .0; let result = self.const_eval_discriminant(EnumVariantId { parent: enum_id, @@ -1122,7 +1094,7 @@ impl Evaluator<'_> { } } Rvalue::Repeat(x, len) => { - let len = match try_const_usize(self.db, len) { + let len = match try_const_usize(self.db, &len) { Some(x) => x as usize, None => not_supported!("non evaluatable array len in repeat Rvalue"), }; @@ -1154,7 +1126,7 @@ impl Evaluator<'_> { Owned(r) } AggregateKind::Tuple(ty) => { - let layout = self.layout_filled(&ty, locals)?; + let layout = self.layout(&ty)?; Owned(self.make_by_layout( layout.size.bytes_usize(), &layout, @@ -1174,9 +1146,8 @@ impl Evaluator<'_> { Owned(result) } AggregateKind::Adt(x, subst) => { - let subst = self.subst_filler(subst, locals); let (size, variant_layout, tag) = - self.layout_of_variant(*x, subst, locals)?; + self.layout_of_variant(*x, subst.clone(), locals)?; Owned(self.make_by_layout( size, &variant_layout, @@ -1185,7 +1156,7 @@ impl Evaluator<'_> { )?) } AggregateKind::Closure(ty) => { - let layout = self.layout_filled(&ty, locals)?; + let layout = self.layout(&ty)?; Owned(self.make_by_layout( layout.size.bytes_usize(), &layout, @@ -1220,7 +1191,10 @@ impl Evaluator<'_> { // This is no-op Borrowed(self.eval_operand(operand, locals)?) } - x => not_supported!("pointer cast {x:?}"), + PointerCast::ArrayToPointer => { + // We should remove the metadata part if the current type is slice + Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size())) + } }, CastKind::DynStar => not_supported!("dyn star cast"), CastKind::IntToInt @@ -1235,12 +1209,6 @@ impl Evaluator<'_> { 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 => { - let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); - let dest_size = - self.size_of_sized(target_ty, locals, "destination of ptr to ptr cast")?; - Owned(current[0..dest_size].to_vec()) - } CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"), }, }) @@ -1300,8 +1268,8 @@ impl Evaluator<'_> { r.extend(len.to_le_bytes().into_iter()); Owned(r) } - _ => { - not_supported!("slice unsizing from non arrays") + t => { + not_supported!("slice unsizing from non array type {t:?}") } }, } @@ -1327,7 +1295,7 @@ impl Evaluator<'_> { x: VariantId, subst: Substitution, locals: &Locals<'_>, - ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> { + ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { let adt = x.adt_id(); if let DefWithBodyId::VariantId(f) = locals.body.owner { if let VariantId::EnumVariantId(x) = x { @@ -1340,7 +1308,7 @@ impl Evaluator<'_> { } } let layout = self.layout_adt(adt, subst)?; - Ok(match layout.variants { + Ok(match &layout.variants { Variants::Single { .. } => (layout.size.bytes_usize(), layout, None), Variants::Multiple { variants, tag, tag_encoding, .. } => { let cx = self @@ -1357,22 +1325,22 @@ impl Evaluator<'_> { let have_tag = match tag_encoding { TagEncoding::Direct => true, TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => { - if untagged_variant == rustc_enum_variant_idx { + if *untagged_variant == rustc_enum_variant_idx { false } else { discriminant = (variants .iter_enumerated() - .filter(|(x, _)| *x != untagged_variant) + .filter(|(x, _)| x != untagged_variant) .position(|(x, _)| x == rustc_enum_variant_idx) .unwrap() as i128) - .wrapping_add(niche_start as i128); + .wrapping_add(*niche_start as i128); true } } }; ( layout.size.bytes_usize(), - variant_layout, + Arc::new(variant_layout), if have_tag { Some(( layout.fields.offset(0).bytes_usize(), @@ -1419,15 +1387,7 @@ impl Evaluator<'_> { 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::BoundVar(_) => not_supported!("bound var constant"), chalk_ir::ConstValue::InferenceVar(_) => { not_supported!("inference var constant") } @@ -1471,29 +1431,8 @@ impl Evaluator<'_> { self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; Interval::new(addr, size) } - ConstScalar::UnevaluatedConst(const_id, subst) => { - let mut const_id = *const_id; - let mut subst = self.subst_filler(subst, locals); - if let GeneralConstId::ConstId(c) = const_id { - let (c, s) = lookup_impl_const( - self.db, - self.db.trait_environment_for_body(locals.body.owner), - c, - subst, - ); - const_id = GeneralConstId::ConstId(c); - subst = s; - } - let c = self.db.const_eval(const_id.into(), subst).map_err(|e| { - let name = const_id.name(self.db.upcast()); - MirEvalError::ConstEvalError(name, Box::new(e)) - })?; - if let chalk_ir::ConstValue::Concrete(c) = &c.data(Interner).value { - if let ConstScalar::Bytes(_, _) = &c.interned { - return self.allocate_const_in_heap(&c, ty, locals, konst); - } - } - not_supported!("failing at evaluating unevaluated const"); + ConstScalar::UnevaluatedConst(..) => { + not_supported!("unevaluated const present in monomorphized mir"); } ConstScalar::Unknown => not_supported!("evaluating unknown const"), }) @@ -1555,7 +1494,7 @@ impl Evaluator<'_> { } } } - let layout = self.layout_filled(ty, locals); + let layout = self.layout(ty); if self.assert_placeholder_ty_is_unused { if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { return Ok(Some((0, 1))); @@ -1576,122 +1515,12 @@ impl Evaluator<'_> { } } - /// 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, - generics: Option, - } - 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::AssociatedType(id, subst) => { - // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes - // this kind of associated types. - Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone().try_fold_with(self, outer_binder)?, - })) - .intern(Interner)) - } - TyKind::OpaqueType(id, subst) => { - let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); - let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = self.db.infer(func.into()); - let filler = &mut Filler { - db: self.db, - subst: &subst, - generics: Some(generics(self.db.upcast(), func.into())), - }; - 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); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { - not_supported!("missing idx in generics"); - }; - Ok(self - .subst - .as_slice(Interner) - .get(idx) - .and_then(|x| x.ty(Interner)) - .ok_or_else(|| MirEvalError::GenericArgNotProvided(x, self.subst.clone()))? - .clone()) - } - } - let g_def = match owner { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, - DefWithBodyId::ConstId(f) => Some(f.into()), - DefWithBodyId::VariantId(f) => Some(f.into()), - }; - let generics = g_def.map(|g_def| generics(self.db.upcast(), g_def)); - let filler = &mut Filler { db: self.db, subst, generics }; - Ok(normalize( - self.db, - self.trait_env.clone(), - ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?, - )) - } - fn heap_allocate(&mut self, size: usize, _align: usize) -> Address { let pos = self.heap.len(); self.heap.extend(iter::repeat(0).take(size)); 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_fn_trait(&self, def: FunctionId) -> Option { use LangItem::*; let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { @@ -1849,21 +1678,24 @@ impl Evaluator<'_> { ) -> Result<()> { let mir_body = self .db - .mir_body_for_closure(closure) + .monomorphized_mir_body_for_closure( + closure, + generic_args.clone(), + self.trait_env.clone(), + ) .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?; - let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned())) + let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() + { + closure_data.addr.to_bytes() + } else { + closure_data.get(self)?.to_owned() + }; + let arg_bytes = iter::once(Ok(closure_data)) .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned()))) .collect::>>()?; - let bytes = self - .interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone()) - .map_err(|e| { - MirEvalError::InFunction( - Either::Right(closure), - Box::new(e), - span, - locals.body.owner, - ) - })?; + let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { + MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) + })?; destination.write_from_bytes(self, &bytes) } @@ -1877,7 +1709,7 @@ impl Evaluator<'_> { span: MirSpan, ) -> Result<()> { let def: CallableDefId = from_chalk(self.db, def); - let generic_args = self.subst_filler(generic_args, &locals); + let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { if let Some(_) = self.detect_fn_trait(def) { @@ -1982,14 +1814,14 @@ impl Evaluator<'_> { span: MirSpan, destination: Interval, ) -> Result<()> { - 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))?; - let result = self - .interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args) - .map_err(|e| { - MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) - })?; + let mir_body = self + .db + .monomorphized_mir_body(def, generic_args, self.trait_env.clone()) + .map_err(|e| MirEvalError::MirLowerError(imp, e))?; + let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { + MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) + })?; destination.write_from_bytes(self, &result)?; Ok(()) } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 9ff58b27bb47f..e05004eeb6a86 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -1,6 +1,8 @@ //! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation //! is not available. +use std::cmp; + use super::*; macro_rules! from_bytes { @@ -254,6 +256,7 @@ impl Evaluator<'_> { } _ => not_supported!("write to arbitrary file descriptor"), } + destination.write_from_interval(self, len.interval)?; Ok(()) } "pthread_key_create" => { @@ -437,7 +440,7 @@ impl Evaluator<'_> { let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; - let align = self.layout_filled(ty, locals)?.align.abi.bytes(); + let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "needs_drop" => { @@ -456,6 +459,22 @@ impl Evaluator<'_> { let ans = lhs.get(self)? == rhs.get(self)?; destination.write_from_bytes(self, &[u8::from(ans)]) } + "saturating_add" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("saturating_add args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.saturating_add(rhs); + let bits = destination.size * 8; + // FIXME: signed + let is_signed = false; + let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 }; + // FIXME: signed + let mn: u128 = 0; + let ans = cmp::min(mx, cmp::max(mn, ans)); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } "wrapping_add" | "unchecked_add" => { let [lhs, rhs] = args else { return Err(MirEvalError::TypeError("wrapping_add args are not provided")); @@ -474,6 +493,15 @@ impl Evaluator<'_> { let ans = lhs.wrapping_sub(rhs); destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } + "wrapping_mul" | "unchecked_mul" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_mul args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_mul(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } "unchecked_rem" => { // FIXME: signed let [lhs, rhs] = args else { @@ -498,7 +526,7 @@ impl Evaluator<'_> { })?; destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } - "add_with_overflow" => { + "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { let [lhs, rhs] = args else { return Err(MirEvalError::TypeError("const_eval_select args are not provided")); }; @@ -511,8 +539,14 @@ impl Evaluator<'_> { self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?; let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); - let ans = lhs.wrapping_add(rhs); - let is_overflow = false; + let (ans, u128overflow) = match as_str { + "add_with_overflow" => lhs.overflowing_add(rhs), + "sub_with_overflow" => lhs.overflowing_sub(rhs), + "mul_with_overflow" => lhs.overflowing_mul(rhs), + _ => unreachable!(), + }; + let is_overflow = u128overflow + || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255); let is_overflow = vec![u8::from(is_overflow)]; let layout = self.layout(&result_ty)?; let result = self.make_by_layout( diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 6a9d3c5b4f3f7..453c93de8e765 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -21,10 +21,15 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr } _ => None, }) - .unwrap(); - let body = - db.mir_body(func_id.into()).map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; - let (result, stdout, stderr) = interpret_mir(db, &body, Substitution::empty(Interner), false); + .expect("no main function found"); + let body = db + .monomorphized_mir_body( + func_id.into(), + Substitution::empty(Interner), + db.trait_environment(func_id.into()), + ) + .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; + let (result, stdout, stderr) = interpret_mir(db, &body, false); result?; Ok((stdout, stderr)) } @@ -34,7 +39,8 @@ fn check_pass(ra_fixture: &str) { } fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) { - let (db, file_id) = TestDB::with_single_file(ra_fixture); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); let x = eval_main(&db, file_id); match x { Err(e) => { @@ -270,6 +276,243 @@ fn main() { ); } +#[test] +fn from_fn() { + check_pass( + r#" +//- minicore: fn, iterator +struct FromFn(F); + +impl Option> Iterator for FromFn { + type Item = T; + + fn next(&mut self) -> Option { + (self.0)() + } +} + +fn main() { + let mut tokenize = { + FromFn(move || Some(2)) + }; + let s = tokenize.next(); +} + "#, + ); +} + +#[test] +fn for_loop() { + check_pass( + r#" +//- minicore: iterator, add +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +struct X; +struct XIter(i32); + +impl IntoIterator for X { + type Item = i32; + + type IntoIter = XIter; + + fn into_iter(self) -> Self::IntoIter { + XIter(0) + } +} + +impl Iterator for XIter { + type Item = i32; + + fn next(&mut self) -> Option { + if self.0 == 5 { + None + } else { + self.0 += 1; + Some(self.0) + } + } +} + +fn main() { + let mut s = 0; + for x in X { + s += x; + } + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn field_with_associated_type() { + check_pass( + r#" +//- /b/mod.rs crate:b +pub trait Tr { + fn f(self); +} + +pub trait Tr2 { + type Ty: Tr; +} + +pub struct S { + pub t: T::Ty, +} + +impl S { + pub fn g(&self) { + let k = (self.t, self.t); + self.t.f(); + } +} + +//- /a/mod.rs crate:a deps:b +use b::{Tr, Tr2, S}; + +struct A(i32); +struct B(u8); + +impl Tr for A { + fn f(&self) { + } +} + +impl Tr2 for B { + type Ty = A; +} + +#[test] +fn main() { + let s: S = S { t: A(2) }; + s.g(); +} + "#, + ); +} + +#[test] +fn specialization_array_clone() { + check_pass( + r#" +//- minicore: copy, derive, slice, index, coerce_unsized +impl Clone for [T; N] { + #[inline] + fn clone(&self) -> Self { + SpecArrayClone::clone(self) + } +} + +trait SpecArrayClone: Clone { + fn clone(array: &[Self; N]) -> [Self; N]; +} + +impl SpecArrayClone for T { + #[inline] + default fn clone(array: &[T; N]) -> [T; N] { + // FIXME: panic here when we actually implement specialization. + from_slice(array) + } +} + +fn from_slice(s: &[T]) -> [T; N] { + [s[0]; N] +} + +impl SpecArrayClone for T { + #[inline] + fn clone(array: &[T; N]) -> [T; N] { + *array + } +} + +#[derive(Clone, Copy)] +struct X(i32); + +fn main() { + let ar = [X(1), X(2)]; + ar.clone(); +} + "#, + ); +} + +#[test] +fn short_circuit_operator() { + check_pass( + r#" +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + if false && should_not_reach() { + should_not_reach(); + } + true || should_not_reach(); + +} + "#, + ); +} + +#[test] +fn closure_state() { + check_pass( + r#" +//- minicore: fn, add, copy +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut x = 2; + let mut c = move || { + x += 1; + x + }; + c(); + c(); + c(); + if x != 2 { + should_not_reach(); + } + if c() != 6 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn closure_capture_array_const_generic() { + check_pass( + r#" +//- minicore: fn, add, copy +struct X(i32); + +fn f(mut x: [X; N]) { // -> impl FnOnce() { + let c = || { + x; + }; + c(); +} + +fn main() { + let s = f([X(1)]); + //s(); +} + "#, + ); +} + #[test] fn posix_tls() { check_pass( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index b77e2c1c80193..6fe157f45cfe6 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -8,14 +8,14 @@ use hir_def::{ body::Body, data::adt::{StructKind, VariantData}, hir::{ - ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, - Pat, PatId, RecordFieldPat, RecordLitField, + ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, + LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, path::Path, - resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, + resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, - TraitId, + TraitId, TypeOrConstParamId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -29,9 +29,10 @@ use crate::{ display::HirDisplay, infer::{CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, - layout::{layout_of_ty, LayoutError}, + layout::LayoutError, mapping::ToChalk, static_lifetime, + traits::FnTrait, utils::{generics, ClosureSubst}, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, }; @@ -41,8 +42,6 @@ use super::*; mod as_place; mod pattern_matching; -use pattern_matching::AdtPatternShape; - #[derive(Debug, Clone)] struct LoopBlocks { begin: BasicBlockId, @@ -74,6 +73,7 @@ pub enum MirLowerError { ConstEvalError(String, Box), LayoutError(LayoutError), IncompleteExpr, + IncompletePattern, /// Trying to lower a trait function, instead of an implementation TraitFunctionDefinition(TraitId, Name), UnresolvedName(String), @@ -96,6 +96,9 @@ pub enum MirLowerError { UnresolvedLabel, UnresolvedUpvar(Place), UnaccessableLocal, + + // monomorphization errors: + GenericArgNotProvided(TypeOrConstParamId, Substitution), } impl MirLowerError { @@ -129,9 +132,24 @@ impl MirLowerError { e.actual.display(db), )?; } + MirLowerError::GenericArgNotProvided(id, subst) => { + let parent = id.parent; + let param = &db.generic_params(parent).type_or_consts[id.local_id]; + writeln!( + f, + "Generic arg not provided for {}", + param.name().unwrap_or(&Name::missing()).display(db.upcast()) + )?; + writeln!(f, "Provided args: [")?; + for g in subst.iter(Interner) { + write!(f, " {},", g.display(db).to_string())?; + } + writeln!(f, "]")?; + } MirLowerError::LayoutError(_) | MirLowerError::UnsizedTemporary(_) | MirLowerError::IncompleteExpr + | MirLowerError::IncompletePattern | MirLowerError::UnaccessableLocal | MirLowerError::TraitFunctionDefinition(_, _) | MirLowerError::UnresolvedName(_) @@ -528,61 +546,6 @@ impl<'ctx> MirLowerCtx<'ctx> { 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_without_adjust(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_without_adjust(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(), current, expr_id.into())?.into(); - let option_item_place: Place = self.temp(option_item_ty.clone(), current, expr_id.into())?.into(); - let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty, current, expr_id.into())?.into(); - let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())? - 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, place, label, expr_id.into(), |this, begin| { - let Some(current) = this.lower_call(iter_next_fn_op, Box::new([Operand::Copy(ref_mut_iterator_place)]), option_item_place.clone(), begin, false, expr_id.into())? - 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), - AdtPatternShape::Tuple { args: &[pat], ellipsis: None }, - )?; - if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { - let block = this.pop_drop_scope(block); - this.set_goto(block, begin, expr_id.into()); - } - Ok(()) - }) - }, Expr::Call { callee, args, .. } => { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { @@ -918,6 +881,27 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { return Ok(None); }; + if let hir_def::hir::BinaryOp::LogicOp(op) = op { + let value_to_short = match op { + syntax::ast::LogicOp::And => 0, + syntax::ast::LogicOp::Or => 1, + }; + let start_of_then = self.new_basic_block(); + self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into()); + let end_of_then = Some(start_of_then); + let start_of_else = self.new_basic_block(); + let end_of_else = + self.lower_expr_to_place(*rhs, place, start_of_else)?; + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: lhs_op, + targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else), + }, + expr_id.into(), + ); + return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())); + } let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { return Ok(None); }; @@ -1135,8 +1119,39 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(()) } + fn lower_literal_or_const_to_operand( + &mut self, + ty: Ty, + loc: &LiteralOrConst, + ) -> Result { + match loc { + LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l), + LiteralOrConst::Const(c) => { + let unresolved_name = || MirLowerError::unresolved_path(self.db, c); + let resolver = self.owner.resolver(self.db.upcast()); + let pr = resolver + .resolve_path_in_value_ns(self.db.upcast(), c) + .ok_or_else(unresolved_name)?; + match pr { + ResolveValueResult::ValueNs(v) => { + if let ValueNs::ConstId(c) = v { + self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) + } else { + not_supported!("bad path in range pattern"); + } + } + ResolveValueResult::Partial(_, _) => { + not_supported!("associated constants in range pattern") + } + } + } + } + } + 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())? + let size = self + .db + .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())? .size .bytes_usize(); let bytes = match l { @@ -1196,6 +1211,17 @@ impl<'ctx> MirLowerCtx<'ctx> { span: MirSpan, ty: Ty, ) -> Result<()> { + let c = self.lower_const_to_operand(subst, const_id, ty)?; + self.push_assignment(prev_block, place, c.into(), span); + Ok(()) + } + + fn lower_const_to_operand( + &mut self, + subst: Substitution, + const_id: GeneralConstId, + ty: Ty, + ) -> Result { let c = if subst.len(Interner) != 0 { // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) @@ -1205,18 +1231,7 @@ impl<'ctx> MirLowerCtx<'ctx> { .const_eval(const_id.into(), subst) .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? }; - 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(()) + Ok(Operand::Constant(c)) } fn write_bytes_to_place( @@ -1673,8 +1688,23 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { }, (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, - (TyKind::Raw(..) | TyKind::Ref(..), TyKind::Raw(..) | TyKind::Ref(..)) => { - CastKind::PtrToPtr + (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { + CastKind::Pointer(if a == b { + PointerCast::MutToConstPointer + } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str) + && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str) + { + // slice to slice cast is no-op (metadata is not touched), so we use this + PointerCast::MutToConstPointer + } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { + PointerCast::Unsize + } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { + PointerCast::ArrayToPointer + } else { + // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant + // for it in `PointerCast` so we use `MutToConstPointer` + PointerCast::MutToConstPointer + }) } // Enum to int casts (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { @@ -1697,11 +1727,19 @@ pub fn mir_body_for_closure_query( let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else { implementation_error!("closure expression is not closure"); }; - let (captures, _) = infer.closure_info(&closure); + let (captures, kind) = infer.closure_info(&closure); let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); // 0 is return local ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); - let closure_local = ctx.result.locals.alloc(Local { ty: infer[expr].clone() }); + let closure_local = ctx.result.locals.alloc(Local { + ty: match kind { + FnTrait::FnOnce => infer[expr].clone(), + FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone()) + .intern(Interner), + FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone()) + .intern(Interner), + }, + }); ctx.result.param_locals.push(closure_local); let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { implementation_error!("closure has not callable sig"); @@ -1721,6 +1759,10 @@ pub fn mir_body_for_closure_query( } let mut err = None; let closure_local = ctx.result.locals.iter().nth(1).unwrap().0; + let closure_projection = match kind { + FnTrait::FnOnce => vec![], + FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], + }; ctx.result.walk_places(|p| { if let Some(x) = upvar_map.get(&p.local) { let r = x.iter().find(|x| { @@ -1743,7 +1785,8 @@ pub fn mir_body_for_closure_query( match r { Some(x) => { p.local = closure_local; - let mut next_projs = vec![PlaceElem::TupleOrClosureField(x.1)]; + let mut next_projs = closure_projection.clone(); + next_projs.push(PlaceElem::TupleOrClosureField(x.1)); let prev_projs = mem::take(&mut p.projection); if x.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 00864907ac887..ee2a0306d5ee9 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -1,6 +1,6 @@ //! MIR lowering for patterns -use hir_def::resolver::HasResolver; +use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId}; use crate::utils::pattern_matching_dereference_count; @@ -38,7 +38,7 @@ impl MirLowerCtx<'_> { mut binding_mode: BindingAnnotation, ) -> Result<(BasicBlockId, Option)> { Ok(match &self.body.pats[pattern] { - Pat::Missing => return Err(MirLowerError::IncompleteExpr), + Pat::Missing => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); @@ -106,9 +106,92 @@ impl MirLowerCtx<'_> { AdtPatternShape::Record { args: &*args }, )? } - Pat::Range { .. } => not_supported!("range pattern"), + Pat::Range { start, end } => { + let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> { + let lv = self.lower_literal_or_const_to_operand(cond_ty.clone(), l)?; + let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); + let next = self.new_basic_block(); + let discr: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(binop, lv, Operand::Copy(cond_place.clone())), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, else_target), + }, + pattern.into(), + ); + current = next; + Ok(()) + }; + if let Some(start) = start { + add_check(start, BinOp::Le)?; + } + if let Some(end) = end { + add_check(end, BinOp::Ge)?; + } + (current, current_else) + } Pat::Slice { prefix, slice, suffix } => { pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); + if let TyKind::Slice(_) = cond_ty.kind(Interner) { + let pattern_len = prefix.len() + suffix.len(); + let place_len: Place = + self.temp(TyBuilder::usize(), current, pattern.into())?.into(); + self.push_assignment( + current, + place_len.clone(), + Rvalue::Len(cond_place.clone()), + pattern.into(), + ); + let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); + let next = self.new_basic_block(); + if slice.is_none() { + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(place_len), + targets: SwitchTargets::static_if( + pattern_len as u128, + next, + else_target, + ), + }, + pattern.into(), + ); + } else { + let c = Operand::from_concrete_const( + pattern_len.to_le_bytes().to_vec(), + MemoryMap::default(), + TyBuilder::usize(), + ); + let discr: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, else_target), + }, + pattern.into(), + ); + } + current = next; + } for (i, &pat) in prefix.iter().enumerate() { let next_place = cond_place.project(ProjectionElem::ConstantIndex { offset: i as u64, @@ -174,53 +257,44 @@ impl MirLowerCtx<'_> { let pr = resolver .resolve_path_in_value_ns(self.db.upcast(), p) .ok_or_else(unresolved_name)?; - match pr { - ResolveValueResult::ValueNs(v) => match v { - ValueNs::ConstId(c) => { - let tmp: Place = - self.temp(cond_ty.clone(), current, pattern.into())?.into(); - let span = pattern.into(); - self.lower_const( - c.into(), - current, - tmp.clone(), - Substitution::empty(Interner), - span, - cond_ty.clone(), - )?; - let tmp2: Place = - self.temp(TyBuilder::bool(), current, pattern.into())?.into(); - self.push_assignment( - current, - tmp2.clone(), - Rvalue::CheckedBinaryOp( - BinOp::Eq, - Operand::Copy(tmp), - Operand::Copy(cond_place), - ), - span, - ); - let next = self.new_basic_block(); - let else_target = - current_else.unwrap_or_else(|| self.new_basic_block()); - self.set_terminator( - current, - TerminatorKind::SwitchInt { - discr: Operand::Copy(tmp2), - targets: SwitchTargets::static_if(1, next, else_target), - }, - span, - ); - (next, Some(else_target)) + let (c, subst) = 'b: { + if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) { + if let AssocItemId::ConstId(c) = x.0 { + break 'b (c, x.1); } - _ => not_supported!( - "path in pattern position that is not const or variant" - ), - }, - ResolveValueResult::Partial(_, _) => { - not_supported!("assoc const in patterns") } - } + if let ResolveValueResult::ValueNs(v) = pr { + if let ValueNs::ConstId(c) = v { + break 'b (c, Substitution::empty(Interner)); + } + } + not_supported!("path in pattern position that is not const or variant") + }; + let tmp: Place = self.temp(cond_ty.clone(), current, pattern.into())?.into(); + let span = pattern.into(); + self.lower_const(c.into(), current, tmp.clone(), subst, span, cond_ty.clone())?; + let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + tmp2.clone(), + Rvalue::CheckedBinaryOp( + BinOp::Eq, + Operand::Copy(tmp), + Operand::Copy(cond_place), + ), + span, + ); + let next = self.new_basic_block(); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(tmp2), + targets: SwitchTargets::static_if(1, next, else_target), + }, + span, + ); + (next, Some(else_target)) } }, Pat::Lit(l) => match &self.body.exprs[*l] { diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs new file mode 100644 index 0000000000000..b17ac58365699 --- /dev/null +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -0,0 +1,369 @@ +//! Monomorphization of mir, which is used in mir interpreter and const eval. +//! +//! The job of monomorphization is: +//! * Monomorphization. That is, replacing `Option` with `Option` where `T:=i32` substitution +//! is provided +//! * Normalizing types, for example replacing RPIT of other functions called in this body. +//! +//! So the monomorphization should be called even if the substitution is empty. + +use std::mem; + +use chalk_ir::{ + fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, + ConstData, DebruijnIndex, +}; +use hir_def::{DefWithBodyId, GeneralConstId}; +use triomphe::Arc; + +use crate::{ + consteval::unknown_const, + db::HirDatabase, + from_placeholder_idx, + infer::normalize, + method_resolution::lookup_impl_const, + utils::{generics, Generics}, + ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, +}; + +use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind}; + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +struct Filler<'a> { + db: &'a dyn HirDatabase, + trait_env: Arc, + subst: &'a Substitution, + generics: Option, + owner: DefWithBodyId, +} +impl FallibleTypeFolder for Filler<'_> { + type Error = MirLowerError; + + 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::AssociatedType(id, subst) => { + // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes + // this kind of associated types. + Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { + associated_ty_id: *id, + substitution: subst.clone().try_fold_with(self, outer_binder)?, + })) + .intern(Interner)) + } + TyKind::OpaqueType(id, subst) => { + let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); + let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db.infer(func.into()); + let filler = &mut Filler { + db: self.db, + owner: self.owner, + trait_env: self.trait_env.clone(), + subst: &subst, + generics: Some(generics(self.db.upcast(), func.into())), + }; + 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_const( + &mut self, + _ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> std::result::Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + not_supported!("missing idx in generics"); + }; + Ok(self + .subst + .as_slice(Interner) + .get(idx) + .and_then(|x| x.constant(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .clone()) + } + + 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); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + not_supported!("missing idx in generics"); + }; + Ok(self + .subst + .as_slice(Interner) + .get(idx) + .and_then(|x| x.ty(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .clone()) + } + + fn try_fold_const( + &mut self, + constant: chalk_ir::Const, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let next_ty = normalize( + self.db, + self.trait_env.clone(), + constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?, + ); + ConstData { ty: next_ty, value: constant.data(Interner).value.clone() } + .intern(Interner) + .try_super_fold_with(self, outer_binder) + } +} + +impl Filler<'_> { + fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> { + let tmp = mem::replace(ty, TyKind::Error.intern(Interner)); + *ty = normalize( + self.db, + self.trait_env.clone(), + tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?, + ); + Ok(()) + } + + fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> { + let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone())); + *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; + Ok(()) + } + + fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> { + let tmp = mem::replace(ty, Substitution::empty(Interner)); + *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; + Ok(()) + } + + fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> { + match op { + Operand::Constant(c) => { + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(b) => { + let resolved = self + .subst + .as_slice(Interner) + .get(b.index) + .ok_or_else(|| { + MirLowerError::GenericArgNotProvided( + self.generics + .as_ref() + .and_then(|x| x.iter().nth(b.index)) + .unwrap() + .0, + self.subst.clone(), + ) + })? + .assert_const_ref(Interner); + *c = resolved.clone(); + } + chalk_ir::ConstValue::InferenceVar(_) + | chalk_ir::ConstValue::Placeholder(_) => {} + chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { + crate::ConstScalar::UnevaluatedConst(const_id, subst) => { + let mut const_id = *const_id; + let mut subst = subst.clone(); + self.fill_subst(&mut subst)?; + if let GeneralConstId::ConstId(c) = const_id { + let (c, s) = lookup_impl_const( + self.db, + self.db.trait_environment_for_body(self.owner), + c, + subst, + ); + const_id = GeneralConstId::ConstId(c); + subst = s; + } + let result = + self.db.const_eval(const_id.into(), subst).map_err(|e| { + let name = const_id.name(self.db.upcast()); + MirLowerError::ConstEvalError(name, Box::new(e)) + })?; + *c = result; + } + crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (), + }, + } + self.fill_const(c)?; + } + Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (), + } + Ok(()) + } + + fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> { + for (_, l) in body.locals.iter_mut() { + self.fill_ty(&mut l.ty)?; + } + for (_, bb) in body.basic_blocks.iter_mut() { + for statement in &mut bb.statements { + match &mut statement.kind { + StatementKind::Assign(_, r) => match r { + Rvalue::Aggregate(ak, ops) => { + for op in &mut **ops { + self.fill_operand(op)?; + } + match ak { + super::AggregateKind::Array(ty) + | super::AggregateKind::Tuple(ty) + | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?, + super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?, + super::AggregateKind::Union(_, _) => (), + } + } + Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => { + self.fill_ty(ty)?; + } + Rvalue::Use(op) => { + self.fill_operand(op)?; + } + Rvalue::Repeat(op, len) => { + self.fill_operand(op)?; + self.fill_const(len)?; + } + Rvalue::Ref(_, _) + | Rvalue::Len(_) + | Rvalue::Cast(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + | Rvalue::UnaryOp(_, _) + | Rvalue::Discriminant(_) + | Rvalue::CopyForDeref(_) => (), + }, + StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + if let Some(terminator) = &mut bb.terminator { + match &mut terminator.kind { + TerminatorKind::Call { func, args, .. } => { + self.fill_operand(func)?; + for op in &mut **args { + self.fill_operand(op)?; + } + } + TerminatorKind::SwitchInt { discr, .. } => { + self.fill_operand(discr)?; + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => (), + } + } + } + Ok(()) + } +} + +pub fn monomorphized_mir_body_query( + db: &dyn HirDatabase, + owner: DefWithBodyId, + subst: Substitution, + trait_env: Arc, +) -> Result, MirLowerError> { + let g_def = match owner { + DefWithBodyId::FunctionId(f) => Some(f.into()), + DefWithBodyId::StaticId(_) => None, + DefWithBodyId::ConstId(f) => Some(f.into()), + DefWithBodyId::VariantId(f) => Some(f.into()), + }; + let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let body = db.mir_body(owner)?; + let mut body = (*body).clone(); + filler.fill_body(&mut body)?; + Ok(Arc::new(body)) +} + +pub fn monomorphized_mir_body_recover( + _: &dyn HirDatabase, + _: &[String], + _: &DefWithBodyId, + _: &Substitution, + _: &Arc, +) -> Result, MirLowerError> { + return Err(MirLowerError::Loop); +} + +pub fn monomorphized_mir_body_for_closure_query( + db: &dyn HirDatabase, + closure: ClosureId, + subst: Substitution, + trait_env: Arc, +) -> Result, MirLowerError> { + let (owner, _) = db.lookup_intern_closure(closure.into()); + let g_def = match owner { + DefWithBodyId::FunctionId(f) => Some(f.into()), + DefWithBodyId::StaticId(_) => None, + DefWithBodyId::ConstId(f) => Some(f.into()), + DefWithBodyId::VariantId(f) => Some(f.into()), + }; + let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let body = db.mir_body_for_closure(closure)?; + let mut body = (*body).clone(); + filler.fill_body(&mut body)?; + Ok(Arc::new(body)) +} + +// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query. +pub fn monomorphize_mir_body_bad( + db: &dyn HirDatabase, + mut body: MirBody, + subst: Substitution, + trait_env: Arc, +) -> Result { + let owner = body.owner; + let g_def = match owner { + DefWithBodyId::FunctionId(f) => Some(f.into()), + DefWithBodyId::StaticId(_) => None, + DefWithBodyId::ConstId(f) => Some(f.into()), + DefWithBodyId::VariantId(f) => Some(f.into()), + }; + let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + filler.fill_body(&mut body)?; + Ok(body) +} diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 3821e6a59c2d7..58662b01b99b1 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -437,6 +437,6 @@ impl<'a> MirPrettyCtx<'a> { } fn hir_display(&self, ty: &'a T) -> impl Display + 'a { - ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithId) + ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst) } } diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 772bd3e536ece..111ac0b618eb2 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -140,6 +140,7 @@ fn infer_path_qualified_macros_expanded() { fn expr_macro_def_expanded_in_various_places() { check_infer( r#" + //- minicore: iterator macro spam() { 1isize } @@ -195,8 +196,17 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} + 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter + 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': ! + 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter + 100..119 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 100..119 'for _ ...!() {}': Option>> 100..119 'for _ ...!() {}': () - 104..105 '_': {unknown} + 100..119 'for _ ...!() {}': () + 100..119 'for _ ...!() {}': () + 104..105 '_': Iterator::Item> 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': () @@ -221,6 +231,7 @@ fn expr_macro_def_expanded_in_various_places() { fn expr_macro_rules_expanded_in_various_places() { check_infer( r#" + //- minicore: iterator macro_rules! spam { () => (1isize); } @@ -276,8 +287,17 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} + 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter + 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': ! + 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter + 114..133 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 114..133 'for _ ...!() {}': Option>> + 114..133 'for _ ...!() {}': () + 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': {unknown} + 118..119 '_': Iterator::Item> 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': () diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 09c63f873ee62..59046c0435a3a 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs @@ -327,6 +327,7 @@ fn diverging_expression_2() { fn diverging_expression_3_break() { check_infer_with_mismatches( r" + //- minicore: iterator //- /main.rs fn test1() { // should give type mismatch @@ -360,6 +361,15 @@ fn diverging_expression_3_break() { 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 + 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': ! + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': &mut {unknown} + 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': () + 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} @@ -367,12 +377,30 @@ fn diverging_expression_3_break() { 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 + 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': ! + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': &mut {unknown} + 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': () + 237..250 'for a in b {}': () 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 + 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': ! + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': &mut {unknown} + 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': () + 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index b73f0d72a3f91..d683113d5d65e 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -6,6 +6,7 @@ use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches fn infer_pattern() { check_infer( r#" + //- minicore: iterator fn test(x: &i32) { let y = x; let &z = x; @@ -46,6 +47,15 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &str) 83..84 '1': i32 86..93 '"hello"': &str + 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': ! + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': &mut {unknown} + 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': () + 101..151 'for (e... }': () 101..151 'for (e... }': () 105..111 '(e, f)': ({unknown}, {unknown}) 106..107 'e': {unknown} diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 8f4b807f560b4..a7b0f46e5e54a 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -246,6 +246,7 @@ fn infer_std_crash_5() { // taken from rustc check_infer( r#" + //- minicore: iterator fn extra_compiler_flags() { for content in doesnt_matter { let name = if doesnt_matter { @@ -264,6 +265,15 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () + 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} + 32..320 'for co... }': ! + 32..320 'for co... }': {unknown} + 32..320 'for co... }': &mut {unknown} + 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': () + 32..320 'for co... }': () 32..320 'for co... }': () 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} @@ -1215,6 +1225,7 @@ fn mamba(a: U32!(), p: u32) -> u32 { fn for_loop_block_expr_iterable() { check_infer( r#" +//- minicore: iterator fn test() { for _ in { let x = 0; } { let y = 0; @@ -1223,8 +1234,17 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () + 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': ! + 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()> + 16..66 'for _ ... }': fn next>(&mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> + 16..66 'for _ ... }': Option>> + 16..66 'for _ ... }': () + 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': {unknown} + 20..21 '_': Iterator::Item> 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5392cb6a32a17..38add4eda1ad0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -62,7 +62,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx, TagEncoding}, + layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -961,8 +961,8 @@ impl Field { Type::new(db, var_id, ty) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result { - layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into()) + pub fn layout(&self, db: &dyn HirDatabase) -> Result, LayoutError> { + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -1135,7 +1135,7 @@ impl Enum { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { + pub fn layout(self, db: &dyn HirDatabase) -> Result<(Arc, usize), LayoutError> { let layout = Adt::from(self).layout(db)?; let tag_size = if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.variants { @@ -1219,11 +1219,11 @@ impl Variant { let parent_enum = self.parent_enum(db); let (parent_layout, tag_size) = parent_enum.layout(db)?; Ok(( - match parent_layout.variants { + match &parent_layout.variants { layout::Variants::Multiple { variants, .. } => { variants[RustcEnumVariantIdx(self.id)].clone() } - _ => parent_layout, + _ => (*parent_layout).clone(), }, tag_size, )) @@ -1255,7 +1255,7 @@ impl Adt { }) } - pub fn layout(self, db: &dyn HirDatabase) -> Result { + pub fn layout(self, db: &dyn HirDatabase) -> Result, LayoutError> { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } @@ -1949,7 +1949,11 @@ impl Function { db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, ) -> String { - let body = match db.mir_body(self.id.into()) { + let body = match db.monomorphized_mir_body( + self.id.into(), + Substitution::empty(Interner), + db.trait_environment(self.id.into()), + ) { Ok(body) => body, Err(e) => { let mut r = String::new(); @@ -1957,8 +1961,7 @@ impl Function { return r; } }; - let (result, stdout, stderr) = - interpret_mir(db, &body, Substitution::empty(Interner), false); + let (result, stdout, stderr) = interpret_mir(db, &body, false); let mut text = match result { Ok(_) => "pass".to_string(), Err(e) => { @@ -4240,8 +4243,8 @@ impl Type { .collect() } - pub fn layout(&self, db: &dyn HirDatabase) -> Result { - layout_of_ty(db, &self.ty, self.env.krate) + pub fn layout(&self, db: &dyn HirDatabase) -> Result, LayoutError> { + db.layout_of_ty(self.ty.clone(), self.env.krate) } } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 62cc7488298ad..7587aea55cfa3 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -4728,6 +4728,7 @@ const fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { let mut x = 5; for _ in 0..10 { @@ -4751,6 +4752,7 @@ fn $0fun_name(x: &mut i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { for _ in 0..10 { let mut x = 5; @@ -4774,6 +4776,7 @@ fn $0fun_name(mut x: i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { loop { let mut x = 5; diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs index 3b79def639df4..2c6cbf6146a95 100644 --- a/crates/ide-completion/src/completions/lifetime.rs +++ b/crates/ide-completion/src/completions/lifetime.rs @@ -329,6 +329,7 @@ fn foo() { fn complete_label_in_for_iterable() { check( r#" +//- minicore: iterator fn foo() { 'outer: for _ in [{ 'inner: loop { break '$0 } }] {} } diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 7baa7b6426821..30576c71fb7c2 100644 --- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -124,12 +124,14 @@ fn foo() { #[test] fn value_break_in_for_loop() { + // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" +//- minicore: iterator fn test() { for _ in [()] { break 3; - // ^^^^^^^ error: can't break with a value in this position + // ^ error: expected (), found i32 } } "#, diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 79fef0dcbd023..887d18b989a14 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -1137,5 +1137,5 @@ fn benchmark_syntax_highlighting_parser() { .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) .count() }; - assert_eq!(hash, 1170); + assert_eq!(hash, 1169); } From c5b974b55da246ee0850f5b38084f93cf5e0b913 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 29 May 2023 00:46:15 +0200 Subject: [PATCH 541/806] Update version attribute for 1.70 lints --- clippy_lints/src/allow_attributes.rs | 2 +- clippy_lints/src/collection_is_never_read.rs | 2 +- clippy_lints/src/large_futures.rs | 2 +- clippy_lints/src/let_with_type_underscore.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/missing_assert_message.rs | 2 +- clippy_lints/src/redundant_async_block.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/allow_attributes.rs b/clippy_lints/src/allow_attributes.rs index add73d0aeeed3..554efdc58e1cb 100644 --- a/clippy_lints/src/allow_attributes.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { /// a.len() /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub ALLOW_ATTRIBUTES, restriction, "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 5e2eb5789f627..ac5ac542cf944 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// println!("{sample}"); /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub COLLECTION_IS_NEVER_READ, nursery, "a collection is never queried" diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 0ca31033b169b..087c4a65250a9 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// wait(fut).await; /// } /// ``` - #[clippy::version = "1.68.0"] + #[clippy::version = "1.70.0"] pub LARGE_FUTURES, pedantic, "large future may lead to unexpected stack overflows" diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index c01e3882d529f..2f10e3d258133 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// ```rust,ignore /// let my_number = 1; /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub LET_WITH_TYPE_UNDERSCORE, complexity, "unneeded underscore type (`_`) in a variable declaration" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9a594d964ab22..88cbefbb5d3d5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3191,7 +3191,7 @@ declare_clippy_lint! { /// let mut v = vec![1, 2, 3]; /// v.clear(); /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub CLEAR_WITH_DRAIN, nursery, "calling `drain` in order to `clear` a container" diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index 2214a568d9c6d..4dbb79334caf3 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub MISSING_ASSERT_MESSAGE, restriction, "checks assertions without a custom panic message" diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index a0f831764d071..05e52e6b38b12 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// }; /// let fut = f; /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub REDUNDANT_ASYNC_BLOCK, complexity, "`async { future.await }` can be replaced by `future`" From 53f1e6b7ef705467faeff60f85db613e82c9b224 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 4 May 2023 10:55:21 +1000 Subject: [PATCH 542/806] Use `Cow` in `{D,Subd}iagnosticMessage`. Each of `{D,Subd}iagnosticMessage::{Str,Eager}` has a comment: ``` // FIXME(davidtwco): can a `Cow<'static, str>` be used here? ``` This commit answers that question in the affirmative. It's not the most compelling change ever, but it might be worth merging. This requires changing the `impl<'a> From<&'a str>` impls to `impl From<&'static str>`, which involves a bunch of knock-on changes that require/result in call sites being a little more precise about exactly what kind of string they use to create errors, and not just `&str`. This will result in fewer unnecessary allocations, though this will not have any notable perf effects given that these are error paths. Note that I was lazy within Clippy, using `to_string` in a few places to preserve the existing string imprecision. I could have used `impl Into<{D,Subd}iagnosticMessage>` in various places as is done in the compiler, but that would have required changes to *many* call sites (mostly changing `&format("...")` to `format!("...")`) which didn't seem worthwhile. --- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 11 ++++------- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_utils/src/diagnostics.rs | 22 ++++++++++++---------- clippy_utils/src/sugg.rs | 6 +++--- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index f1831a30461af..3b7eccad79df8 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.sess.span_err(span, err.as_ref()); + cx.tcx.sess.span_err(span, err); } } else { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 0bb1775aae9cf..7d53fe65658a2 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -26,7 +26,6 @@ use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -240,9 +239,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{x}` to")), - ) - .as_ref(), + |x| format!("change `{x}` to"), + ), suggestion, Applicability::Unspecified, ); @@ -270,9 +268,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{x}` to")) - ) - .as_ref(), + |x| format!("change `{x}` to") + ), suggestion, Applicability::Unspecified, ); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 8b0e0ce5a3001..5073eb02bd84a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -163,7 +163,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { diag.span_suggestion( fn_decl.output.span(), - return_type_sugg_msg.as_str(), + return_type_sugg_msg, return_type_sugg, Applicability::MaybeIncorrect, ); diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 812f6fe71a0a0..edd87546a5f88 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -46,7 +46,7 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: &str) { - cx.struct_span_lint(lint, sp, msg, |diag| { + cx.struct_span_lint(lint, sp, msg.to_string(), |diag| { docs_link(diag, lint); diag }); @@ -80,11 +80,12 @@ pub fn span_lint_and_help( help_span: Option, help: &str, ) { - cx.struct_span_lint(lint, span, msg, |diag| { + cx.struct_span_lint(lint, span, msg.to_string(), |diag| { + let help = help.to_string(); if let Some(help_span) = help_span { - diag.span_help(help_span, help); + diag.span_help(help_span, help.to_string()); } else { - diag.help(help); + diag.help(help.to_string()); } docs_link(diag, lint); diag @@ -122,7 +123,8 @@ pub fn span_lint_and_note( note_span: Option, note: &str, ) { - cx.struct_span_lint(lint, span, msg, |diag| { + cx.struct_span_lint(lint, span, msg.to_string(), |diag| { + let note = note.to_string(); if let Some(note_span) = note_span { diag.span_note(note_span, note); } else { @@ -143,7 +145,7 @@ where S: Into, F: FnOnce(&mut Diagnostic), { - cx.struct_span_lint(lint, sp, msg, |diag| { + cx.struct_span_lint(lint, sp, msg.to_string(), |diag| { f(diag); docs_link(diag, lint); diag @@ -151,7 +153,7 @@ where } pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { - cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| { + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| { docs_link(diag, lint); diag }); @@ -165,7 +167,7 @@ pub fn span_lint_hir_and_then( msg: &str, f: impl FnOnce(&mut Diagnostic), ) { - cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| { + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| { f(diag); docs_link(diag, lint); diag @@ -202,7 +204,7 @@ pub fn span_lint_and_sugg( applicability: Applicability, ) { span_lint_and_then(cx, lint, sp, msg, |diag| { - diag.span_suggestion(sp, help, sugg, applicability); + diag.span_suggestion(sp, help.to_string(), sugg, applicability); }); } @@ -232,5 +234,5 @@ pub fn multispan_sugg_with_applicability( ) where I: IntoIterator, { - diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability); + diag.multipart_suggestion(help_msg.to_string(), sugg.into_iter().collect(), applicability); } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 14f7f03016fbe..f477524eec5cc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -741,7 +741,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { if let Some(indent) = indentation(cx, item) { let span = item.with_hi(item.lo()); - self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability); + self.span_suggestion(span, msg.to_string(), format!("{attr}\n{indent}"), applicability); } } @@ -762,7 +762,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { }) .collect::(); - self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability); + self.span_suggestion(span, msg.to_string(), format!("{new_item}\n{indent}"), applicability); } } @@ -779,7 +779,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { } } - self.span_suggestion(remove_span, msg, "", applicability); + self.span_suggestion(remove_span, msg.to_string(), "", applicability); } } From 57e67e4ab21b466158a3e7eeaf598baefc18ad72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Sat, 27 May 2023 21:25:25 +0800 Subject: [PATCH 543/806] Don't suggest changing {ImmRef,MutRef} implicit self to be mutable --- .../src/diagnostics/mutability_errors.rs | 28 ++++++++++++++---- tests/ui/borrowck/issue-111554.rs | 28 ++++++++++++++++++ tests/ui/borrowck/issue-111554.stderr | 29 +++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 tests/ui/borrowck/issue-111554.rs create mode 100644 tests/ui/borrowck/issue-111554.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index d0e17bf5a0848..34d466db2b409 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -416,12 +416,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _, ) = pat.kind { - err.span_suggestion( - upvar_ident.span, - "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), - Applicability::MachineApplicable, - ); + if upvar_ident.name == kw::SelfLower { + for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) { + if let Some(fn_decl) = node.fn_decl() { + if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) { + err.span_suggestion( + upvar_ident.span, + "consider changing this to be mutable", + format!("mut {}", upvar_ident.name), + Applicability::MachineApplicable, + ); + break; + } + } + } + } else { + err.span_suggestion( + upvar_ident.span, + "consider changing this to be mutable", + format!("mut {}", upvar_ident.name), + Applicability::MachineApplicable, + ); + } } let tcx = self.infcx.tcx; diff --git a/tests/ui/borrowck/issue-111554.rs b/tests/ui/borrowck/issue-111554.rs new file mode 100644 index 0000000000000..0dad55be3acff --- /dev/null +++ b/tests/ui/borrowck/issue-111554.rs @@ -0,0 +1,28 @@ +struct Foo {} + +impl Foo { + pub fn foo(&mut self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + } + + pub fn baz(&self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + //~| ERROR cannot borrow data in a `&` reference as mutable + } + + pub fn qux(mut self) { + || bar(&mut self); + // OK + } + + pub fn quux(self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + } +} + +fn bar(_: &mut Foo) {} + +fn main() {} diff --git a/tests/ui/borrowck/issue-111554.stderr b/tests/ui/borrowck/issue-111554.stderr new file mode 100644 index 0000000000000..6b7a42e495999 --- /dev/null +++ b/tests/ui/borrowck/issue-111554.stderr @@ -0,0 +1,29 @@ +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:5:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:10:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/issue-111554.rs:10:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:21:16 + | +LL | pub fn quux(self) { + | ---- help: consider changing this to be mutable: `mut self` +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0596`. From 4d9303df18802457661e40325e8ae3b24db5073c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 8 May 2023 21:35:16 +0200 Subject: [PATCH 544/806] Changelog for Rust 1.70 :hammer: --- CHANGELOG.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2648d9faa6d7..33c9a318d681c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,132 @@ document. ## Unreleased / Beta / In Rust Nightly -[149392b0...master](https://github.com/rust-lang/rust-clippy/compare/149392b0...master) +[83e42a23...master](https://github.com/rust-lang/rust-clippy/compare/83e42a23...master) + +## Rust 1.70 + +Current beta, released 2023-06-01 + +[149392b0...83e42a23](https://github.com/rust-lang/rust-clippy/compare/149392b0...83e42a23) + +### New Lints + +* [`large_futures`] + [#10414](https://github.com/rust-lang/rust-clippy/pull/10414) +* [`missing_assert_message`] + [#10362](https://github.com/rust-lang/rust-clippy/pull/10362) +* [`clear_with_drain`] + [#10528](https://github.com/rust-lang/rust-clippy/pull/10528) +* [`redundant_async_block`] + [#10448](https://github.com/rust-lang/rust-clippy/pull/10448) +* [`collection_is_never_read`] + [#10415](https://github.com/rust-lang/rust-clippy/pull/10415) +* [`let_with_type_underscore`] + [#10467](https://github.com/rust-lang/rust-clippy/pull/10467) +* [`tests_outside_test_module`] + [#10543](https://github.com/rust-lang/rust-clippy/pull/10543) +* [`allow_attributes`] + [#10481](https://github.com/rust-lang/rust-clippy/pull/10481) +* [`suspicious_doc_comments`] + [#10497](https://github.com/rust-lang/rust-clippy/pull/10497) +* [`unnecessary_box_returns`] + [#9102](https://github.com/rust-lang/rust-clippy/pull/9102) +* [`manual_main_separator_str`] + [#10483](https://github.com/rust-lang/rust-clippy/pull/10483) +* [`unnecessary_struct_initialization`] + [#10489](https://github.com/rust-lang/rust-clippy/pull/10489) +* [`manual_slice_size_calculation`] + [#10601](https://github.com/rust-lang/rust-clippy/pull/10601) +* [`lines_filter_map_ok`] + [#10534](https://github.com/rust-lang/rust-clippy/pull/10534) + +### Moves and Deprecations + +* Moved [`let_underscore_untyped`] to `restriction` + [#10442](https://github.com/rust-lang/rust-clippy/pull/10442) + +### Enhancements + +* [`extra_unused_type_parameters`]: No longer lints on public items if `avoid-breaking-exported-api` is set + [#10536](https://github.com/rust-lang/rust-clippy/pull/10536) +* [`len_without_is_empty`]: Now also detects `async` functions + [#10359](https://github.com/rust-lang/rust-clippy/pull/10359) +* [`arithmetic_side_effects`]: Now correctly handles divisions and modulo expressions if the right-hand-side + is unknown + [#10585](https://github.com/rust-lang/rust-clippy/pull/10585) +* [`nonminimal_bool`]: No longer ignores `#[allow]` attributes + [#10588](https://github.com/rust-lang/rust-clippy/pull/10588) +* [`uninit_vec`], [`uninit_assumed_init`]: Now uses a better heuristic + [#10520](https://github.com/rust-lang/rust-clippy/pull/10520) +* [`ifs_same_cond`]: Now also detects immutable method calls. + [#10350](https://github.com/rust-lang/rust-clippy/pull/10350) +* [`arithmetic_side_effects`]: No longer lints on right or left shifts with constant integers, as the + compiler warns about them + [#10309](https://github.com/rust-lang/rust-clippy/pull/10309) +* [`items_after_statements`]: `#[allow(items_after_statements)]` now works on items + [#10542](https://github.com/rust-lang/rust-clippy/pull/10542) +* [`significant_drop_tightening`]: was optimized + [#10533](https://github.com/rust-lang/rust-clippy/pull/10533) + +### False Positive Fixes + +* [`single_component_path_imports`]: No longer lints if the import is used relative to `self` + [#10566](https://github.com/rust-lang/rust-clippy/pull/10566) +* [`derivable_impls`]: No longer suggests deriving `Default` on generics with implicit arguments + [#10399](https://github.com/rust-lang/rust-clippy/pull/10399) +* [`let_unit_value`]: No longer lints if the expression contains an `await` + [#10439](https://github.com/rust-lang/rust-clippy/pull/10439) +* [`double_must_use`]: Now ignores `async` functions + [#10589](https://github.com/rust-lang/rust-clippy/pull/10589) +* [`manual_clamp`]: No longer lints in constant context + [#10479](https://github.com/rust-lang/rust-clippy/pull/10479) +* [`almost_swapped`]: Now ignores external macros + [#10502](https://github.com/rust-lang/rust-clippy/pull/10502) +* [`nonminimal_bool`]: Now ignores macros + [#10527](https://github.com/rust-lang/rust-clippy/pull/10527) +* [`needless_return`]: No longer lints match statements with incompatible branches + [#10593](https://github.com/rust-lang/rust-clippy/pull/10593) +* [`use_self`]: do not suggest using `Self` in const generic parameters + [#10375](https://github.com/rust-lang/rust-clippy/pull/10375) +* [`mem_replace_option_with_none`]: No longer lints on field expressions + [#10594](https://github.com/rust-lang/rust-clippy/pull/10594) +* [`items_after_statements`]: No longer lints on times from macros + [#10542](https://github.com/rust-lang/rust-clippy/pull/10542) +* [`print_literal`], [`write_literal`]: No longer lint strings coming from the `file!()` macro + [#10573](https://github.com/rust-lang/rust-clippy/pull/10573) +* [`uninit_vec`], [`uninit_assumed_init`]: Now check the types inside arrays and tuples + [#10553](https://github.com/rust-lang/rust-clippy/pull/10553) +* [`almost_swapped`]: No longer lints if a variable is assigned to itself + [#10499](https://github.com/rust-lang/rust-clippy/pull/10499) +* [`missing_docs_in_private_items`]: No longer lints on public items + [#10324](https://github.com/rust-lang/rust-clippy/pull/10324) + +### Suggestion Fixes/Improvements + +* [`extra_unused_type_parameters`]: The suggestion is now machine applicable + [#10536](https://github.com/rust-lang/rust-clippy/pull/10536) +* [`match_single_binding`]: Now adds a semicolon after the suggestion + [#10470](https://github.com/rust-lang/rust-clippy/pull/10470) +* [`missing_const_for_fn`]: Now includes a note if the change could break compatibility + [#10618](https://github.com/rust-lang/rust-clippy/pull/10618) +* [`cast_possible_truncation`]: Corrected suggestion for float and wildcard casts + [#10496](https://github.com/rust-lang/rust-clippy/pull/10496) +* [`transmutes_expressible_as_ptr_casts`]: The suggestion now includes parentheses when they are required + [#10454](https://github.com/rust-lang/rust-clippy/pull/10454) + +### ICE Fixes + +* [`needless_borrow`]: No longer panics on ambiguous projections + [#10403](https://github.com/rust-lang/rust-clippy/pull/10403) +* [`multiple_unsafe_ops_per_block`]: Fix ICE when calling a function-like object in an unsafe block + [#10405](https://github.com/rust-lang/rust-clippy/pull/10405) + +### Others + +* `clippy-driver` now searches parent directories for `clippy.toml` files + [#10592](https://github.com/rust-lang/rust-clippy/pull/10592) +* Fixed a deserialization error for the `array-size-threshold` config value + [#10423](https://github.com/rust-lang/rust-clippy/pull/10423) ## Rust 1.69 From ab9347542c4f584952a5f554a18e1f92188b2fdb Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 28 May 2023 23:11:38 +0900 Subject: [PATCH 545/806] Consider macro files when replacing nodes --- .../convert_named_struct_to_tuple_struct.rs | 95 ++++++++++++++++++- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 9dc1da2461a34..ce31d1d891d1b 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -52,6 +52,9 @@ pub(crate) fn convert_named_struct_to_tuple_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { + // XXX: We don't currently provide this assist for struct definitions inside macros, but if we + // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files + // too. let strukt = ctx.find_node_at_offset::>()?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let record_fields = match field_list { @@ -62,12 +65,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct( Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), }; - let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); acc.add( AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite), "Convert to tuple struct", - target, + strukt.syntax().text_range(), |edit| { edit_field_references(ctx, edit, record_fields.fields()); edit_struct_references(ctx, edit, strukt_def); @@ -82,6 +84,8 @@ fn edit_struct_def( strukt: &Either, record_fields: ast::RecordFieldList, ) { + // Note that we don't need to consider macro files in this function because this this is + // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields .fields() .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?))); @@ -141,8 +145,13 @@ fn edit_struct_references( match_ast! { match node { ast::RecordPat(record_struct_pat) => { + let Some(fr) = ctx.sema.original_range_opt(record_struct_pat.syntax()) else { + // We've found the node to replace, so we should return `Some` even if the + // replacement failed to stop the ancestor node traversal. + return Some(()); + }; edit.replace( - record_struct_pat.syntax().text_range(), + fr.range, ast::make::tuple_struct_pat( record_struct_pat.path()?, record_struct_pat @@ -154,6 +163,10 @@ fn edit_struct_references( ); }, ast::RecordExpr(record_expr) => { + let Some(fr) = ctx.sema.original_range_opt(record_expr.syntax()) else { + // See the comment above. + return Some(()); + }; let path = record_expr.path()?; let args = record_expr .record_expr_field_list()? @@ -161,7 +174,7 @@ fn edit_struct_references( .filter_map(|f| f.expr()) .join(", "); - edit.replace(record_expr.syntax().text_range(), format!("{path}({args})")); + edit.replace(fr.range, format!("{path}({args})")); }, _ => return None, } @@ -199,7 +212,7 @@ fn edit_field_references( if let Some(name_ref) = r.name.as_name_ref() { // Only edit the field reference if it's part of a `.field` access if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { - edit.replace(name_ref.syntax().text_range(), index.to_string()); + edit.replace(r.range, index.to_string()); } } } @@ -813,6 +826,78 @@ use crate::{A::Variant, Inner}; fn f() { let a = Variant(Inner); } +"#, + ); + } + + #[test] + fn field_access_inside_macro_call() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct $0Struct { + inner: i32, +} + +macro_rules! id { + ($e:expr) => { $e } +} + +fn test(c: Struct) { + id!(c.inner); +} +"#, + r#" +struct Struct(i32); + +macro_rules! id { + ($e:expr) => { $e } +} + +fn test(c: Struct) { + id!(c.0); +} +"#, + ) + } + + #[test] + fn struct_usage_inside_macro_call() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} + +struct $0Struct { + inner: i32, +} + +fn test() { + id! { + let s = Struct { + inner: 42, + }; + let Struct { inner: value } = s; + let Struct { inner } = s; + } +} +"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} + +struct Struct(i32); + +fn test() { + id! { + let s = Struct(42); + let Struct(value) = s; + let Struct(inner) = s; + } +} "#, ); } From 033e6ac57a5fb650e6f5240e7d1b8cc7841ff53b Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 29 May 2023 19:44:31 +0900 Subject: [PATCH 546/806] Verify name references more rigidly Previously we didn't verify that record expressions/patterns that were found did actually point to the struct we're operating on. Moreover, when that record expressions/patterns had missing child nodes, we would continue traversing their ancestor nodes. --- .../convert_named_struct_to_tuple_struct.rs | 172 +++++++++++++----- 1 file changed, 124 insertions(+), 48 deletions(-) diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index ce31d1d891d1b..00a4e0530d2b9 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -1,9 +1,9 @@ use either::Either; -use ide_db::defs::Definition; +use ide_db::{defs::Definition, search::FileReference}; use itertools::Itertools; use syntax::{ ast::{self, AstNode, HasGenericParams, HasVisibility}, - match_ast, SyntaxKind, SyntaxNode, + match_ast, SyntaxKind, }; use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; @@ -141,57 +141,70 @@ fn edit_struct_references( }; let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); - let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> { - match_ast! { - match node { - ast::RecordPat(record_struct_pat) => { - let Some(fr) = ctx.sema.original_range_opt(record_struct_pat.syntax()) else { - // We've found the node to replace, so we should return `Some` even if the - // replacement failed to stop the ancestor node traversal. - return Some(()); - }; - edit.replace( - fr.range, - ast::make::tuple_struct_pat( - record_struct_pat.path()?, - record_struct_pat - .record_pat_field_list()? - .fields() - .filter_map(|pat| pat.pat()) - ) - .to_string() - ); - }, - ast::RecordExpr(record_expr) => { - let Some(fr) = ctx.sema.original_range_opt(record_expr.syntax()) else { - // See the comment above. - return Some(()); - }; - let path = record_expr.path()?; - let args = record_expr - .record_expr_field_list()? - .fields() - .filter_map(|f| f.expr()) - .join(", "); - - edit.replace(fr.range, format!("{path}({args})")); - }, - _ => return None, - } - } - Some(()) - }; - for (file_id, refs) in usages { edit.edit_file(file_id); for r in refs { - for node in r.name.syntax().ancestors() { - if edit_node(edit, node).is_some() { - break; - } - } + process_struct_name_reference(ctx, r, edit); + } + } +} + +fn process_struct_name_reference( + ctx: &AssistContext<'_>, + r: FileReference, + edit: &mut SourceChangeBuilder, +) -> Option<()> { + // First check if it's the last semgnet of a path that directly belongs to a record + // expression/pattern. + let name_ref = r.name.as_name_ref()?; + let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; + // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point. + let full_path = + path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap(); + + if full_path.segment().unwrap().name_ref()? != *name_ref { + // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the + // struct we want to edit. + return None; + } + + let parent = full_path.syntax().parent()?; + match_ast! { + match parent { + ast::RecordPat(record_struct_pat) => { + // When we failed to get the original range for the whole struct expression node, + // we can't provide any reasonable edit. Leave it untouched. + let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?; + edit.replace( + file_range.range, + ast::make::tuple_struct_pat( + record_struct_pat.path()?, + record_struct_pat + .record_pat_field_list()? + .fields() + .filter_map(|pat| pat.pat()) + ) + .to_string() + ); + }, + ast::RecordExpr(record_expr) => { + // When we failed to get the original range for the whole struct pattern node, + // we can't provide any reasonable edit. Leave it untouched. + let file_range = ctx.sema.original_range_opt(record_expr.syntax())?; + let path = record_expr.path()?; + let args = record_expr + .record_expr_field_list()? + .fields() + .filter_map(|f| f.expr()) + .join(", "); + + edit.replace(file_range.range, format!("{path}({args})")); + }, + _ => {} } } + + Some(()) } fn edit_field_references( @@ -898,6 +911,69 @@ fn test() { let Struct(inner) = s; } } +"#, + ); + } + + #[test] + fn struct_name_ref_may_not_be_part_of_struct_expr_or_struct_pat() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct $0Struct { + inner: i32, +} +struct Outer { + value: T, +} +fn foo() -> T { loop {} } + +fn test() { + Outer { + value: foo::(); + } +} + +trait HasAssoc { + type Assoc; + fn test(); +} +impl HasAssoc for Struct { + type Assoc = Outer; + fn test() { + let a = Self::Assoc { + value: 42, + }; + let Self::Assoc { value } = a; + } +} +"#, + r#" +struct Struct(i32); +struct Outer { + value: T, +} +fn foo() -> T { loop {} } + +fn test() { + Outer { + value: foo::(); + } +} + +trait HasAssoc { + type Assoc; + fn test(); +} +impl HasAssoc for Struct { + type Assoc = Outer; + fn test() { + let a = Self::Assoc { + value: 42, + }; + let Self::Assoc { value } = a; + } +} "#, ); } From 739530a03c2ac509e1af2cdd758dc3e43b225ff2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 29 May 2023 13:46:10 +0200 Subject: [PATCH 547/806] EarlyBinder::new -> EarlyBinder::bind --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 4 ++-- clippy_utils/src/consts.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a418a910ba8ea..a1d2147cb4965 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1219,7 +1219,7 @@ fn needless_borrow_impl_arg_position<'tcx>( return false; } - let predicate = EarlyBinder::new(predicate).subst(cx.tcx, &substs_with_referent_ty); + let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, &substs_with_referent_ty); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(); infcx.predicate_must_hold_modulo_regions(&obligation) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index af2aac6ac0d0a..c919b4de65dea 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -243,7 +243,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs | ty::Ref(..) | ty::Slice(_) | ty::Tuple(_) => { - format!("<{}>", EarlyBinder::new(ty).subst(cx.tcx, substs)) + format!("<{}>", EarlyBinder::bind(ty).subst(cx.tcx, substs)) }, _ => ty.to_string(), } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index d4cc14bb85632..99f810c27cf82 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -241,7 +241,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) { - item_ty == EarlyBinder::new(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + item_ty == EarlyBinder::bind(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) } else { false } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index fdacfa49e92d5..309d2157b76ee 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -428,7 +428,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< })); if trait_predicates.any(|predicate| { - let predicate = EarlyBinder::new(predicate).subst(cx.tcx, new_subst); + let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, new_subst); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { @@ -438,7 +438,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let output_ty = fn_sig.output(); if output_ty.contains(*param_ty) { if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions( - new_subst, cx.param_env, EarlyBinder::new(output_ty)) { + new_subst, cx.param_env, EarlyBinder::bind(output_ty)) { expr = parent_expr; ty = new_ty; continue; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 843538e1eb2db..cc3183759ae7a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -462,7 +462,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let substs = if self.substs.is_empty() { substs } else { - EarlyBinder::new(substs).subst(self.lcx.tcx, self.substs) + EarlyBinder::bind(substs).subst(self.lcx.tcx, self.substs) }; let result = self From 7dd0ae0262319e850870b5564ffdf53255a47ada Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Mon, 29 May 2023 18:54:58 +0200 Subject: [PATCH 548/806] Change changelog typos Co-authored-by: Takayuki Nakata --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33c9a318d681c..fdf0cacad3624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ Current beta, released 2023-06-01 [#10309](https://github.com/rust-lang/rust-clippy/pull/10309) * [`items_after_statements`]: `#[allow(items_after_statements)]` now works on items [#10542](https://github.com/rust-lang/rust-clippy/pull/10542) -* [`significant_drop_tightening`]: was optimized +* [`significant_drop_tightening`]: Was optimized [#10533](https://github.com/rust-lang/rust-clippy/pull/10533) ### False Positive Fixes @@ -91,7 +91,7 @@ Current beta, released 2023-06-01 [#10527](https://github.com/rust-lang/rust-clippy/pull/10527) * [`needless_return`]: No longer lints match statements with incompatible branches [#10593](https://github.com/rust-lang/rust-clippy/pull/10593) -* [`use_self`]: do not suggest using `Self` in const generic parameters +* [`use_self`]: Do not suggest using `Self` in const generic parameters [#10375](https://github.com/rust-lang/rust-clippy/pull/10375) * [`mem_replace_option_with_none`]: No longer lints on field expressions [#10594](https://github.com/rust-lang/rust-clippy/pull/10594) From 33eef8221db63c6871f5c1957521b40e65eb3c5d Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Mon, 29 May 2023 22:29:50 +0300 Subject: [PATCH 549/806] Avoid ICE on `#![doc(test(...)]` with literal parameter --- compiler/rustc_passes/messages.ftl | 2 ++ compiler/rustc_passes/src/check_attr.rs | 19 +++++++++++++------ compiler/rustc_passes/src/errors.rs | 4 ++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7f9222dac6c4c..139df68bb63d9 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -214,6 +214,8 @@ passes_doc_keyword_only_impl = passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes +passes_doc_test_literal = `#![doc(test(...)]` does not take a literal + passes_doc_test_unknown = unknown `doc(test)` attribute `{$path}` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c3189d1fefe40..c35c7da266429 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -944,21 +944,28 @@ impl CheckAttrVisitor<'_> { let mut is_valid = true; if let Some(metas) = meta.meta_item_list() { for i_meta in metas { - match i_meta.name_or_empty() { - sym::attr | sym::no_crate_inject => {} - _ => { + match (i_meta.name_or_empty(), i_meta.meta_item()) { + (sym::attr | sym::no_crate_inject, _) => {} + (_, Some(m)) => { self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span(), errors::DocTestUnknown { - path: rustc_ast_pretty::pprust::path_to_string( - &i_meta.meta_item().unwrap().path, - ), + path: rustc_ast_pretty::pprust::path_to_string(&m.path), }, ); is_valid = false; } + (_, None) => { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span(), + errors::DocTestLiteral, + ); + is_valid = false; + } } } } else { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 99fc69d1bec7b..ae624dbc9c953 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -281,6 +281,10 @@ pub struct DocTestUnknown { pub path: String, } +#[derive(LintDiagnostic)] +#[diag(passes_doc_test_literal)] +pub struct DocTestLiteral; + #[derive(LintDiagnostic)] #[diag(passes_doc_test_takes_list)] pub struct DocTestTakesList; From 70bbcceaeccc529670f9c9d60cd4fb038ee49fe4 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Mon, 29 May 2023 22:30:09 +0300 Subject: [PATCH 550/806] Add test for `#![doc(test(...)]` with literal parameter --- compiler/rustc_passes/messages.ftl | 4 ++-- tests/ui/attributes/doc-test-literal.rs | 7 +++++++ tests/ui/attributes/doc-test-literal.stderr | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/ui/attributes/doc-test-literal.rs create mode 100644 tests/ui/attributes/doc-test-literal.stderr diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 139df68bb63d9..e76f1614b9334 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -211,11 +211,11 @@ passes_doc_keyword_not_mod = passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks +passes_doc_test_literal = `#![doc(test(...)]` does not take a literal + passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes -passes_doc_test_literal = `#![doc(test(...)]` does not take a literal - passes_doc_test_unknown = unknown `doc(test)` attribute `{$path}` diff --git a/tests/ui/attributes/doc-test-literal.rs b/tests/ui/attributes/doc-test-literal.rs new file mode 100644 index 0000000000000..a06a1afcb3f2f --- /dev/null +++ b/tests/ui/attributes/doc-test-literal.rs @@ -0,0 +1,7 @@ +#![deny(warnings)] + +#![doc(test(""))] +//~^ ERROR `#![doc(test(...)]` does not take a literal +//~^^ WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +fn main() {} diff --git a/tests/ui/attributes/doc-test-literal.stderr b/tests/ui/attributes/doc-test-literal.stderr new file mode 100644 index 0000000000000..ebee09994ba9f --- /dev/null +++ b/tests/ui/attributes/doc-test-literal.stderr @@ -0,0 +1,17 @@ +error: `#![doc(test(...)]` does not take a literal + --> $DIR/doc-test-literal.rs:3:13 + | +LL | #![doc(test(""))] + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 +note: the lint level is defined here + --> $DIR/doc-test-literal.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` + +error: aborting due to previous error + From d3534a65215dc8b0d071baf7208471f2296fc8a7 Mon Sep 17 00:00:00 2001 From: disco07 Date: Tue, 30 May 2023 07:43:10 +0200 Subject: [PATCH 551/806] fix issues 10836 --- clippy_lints/src/booleans.rs | 29 +++++++++++++++++++++++++++++ tests/ui/nonminimal_bool.rs | 16 +++++++++++++++- tests/ui/nonminimal_bool.stderr | 26 ++++++++++---------------- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 455f0df7cd0ad..4bd48185f37e5 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -8,10 +8,12 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; use rustc_span::sym; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does @@ -89,6 +91,27 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { } } +fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.tcx + .lang_items() + .not_trait() + .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) + .and_then(|trait_id| { + cx.tcx.associated_items(trait_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Output"), + ty::AssocKind::Type, + trait_id, + ) + }) + .map_or(false, |assoc_item| { + let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, [])); + let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); + + nty.is_bool() + }) +} + struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } @@ -473,6 +496,12 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { self.bool_expr(e); }, ExprKind::Unary(UnOp::Not, inner) => { + if let ExprKind::Unary(UnOp::Not, ex) = inner.kind { + let ty = self.cx.typeck_results().expr_ty(ex); + if is_impl_not_trait_with_bool_out(self.cx, ty) { + return; + } + } if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() { self.bool_expr(e); } diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 80cc7c60f56e5..54cbbdef22e0f 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -10,6 +10,7 @@ fn main() { let e: bool = unimplemented!(); let _ = !true; let _ = !false; + // vvv Should not lint let _ = !!a; let _ = false || a; // don't lint on cfgs @@ -54,7 +55,6 @@ fn issue4548() { fn check_expect() { let a: bool = unimplemented!(); - #[expect(clippy::nonminimal_bool)] let _ = !!a; } @@ -110,3 +110,17 @@ fn issue_10435() { println!("{}", line!()); } } + +fn issue10836() { + struct Foo(bool); + impl std::ops::Not for Foo { + type Output = bool; + + fn not(self) -> Self::Output { + !self.0 + } + } + + // Should not lint + let _: bool = !!Foo(true); +} diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index 91b5805aa97ab..fa14471301ee1 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -13,37 +13,31 @@ LL | let _ = !false; | ^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:13:13 - | -LL | let _ = !!a; - | ^^^ help: try: `a` - -error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:14:13 + --> $DIR/nonminimal_bool.rs:15:13 | LL | let _ = false || a; | ^^^^^^^^^^ help: try: `a` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:18:13 + --> $DIR/nonminimal_bool.rs:19:13 | LL | let _ = !(!a && b); | ^^^^^^^^^^ help: try: `a || !b` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:19:13 + --> $DIR/nonminimal_bool.rs:20:13 | LL | let _ = !(!a || b); | ^^^^^^^^^^ help: try: `a && !b` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:20:13 + --> $DIR/nonminimal_bool.rs:21:13 | LL | let _ = !a && !(b && c); | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:28:13 + --> $DIR/nonminimal_bool.rs:29:13 | LL | let _ = a == b && c == 5 && a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +50,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:29:13 + --> $DIR/nonminimal_bool.rs:30:13 | LL | let _ = a == b || c == 5 || a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,7 +63,7 @@ LL | let _ = a == b || c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:30:13 + --> $DIR/nonminimal_bool.rs:31:13 | LL | let _ = a == b && c == 5 && b == a; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,7 +76,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:31:13 + --> $DIR/nonminimal_bool.rs:32:13 | LL | let _ = a != b || !(a != b || c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +89,7 @@ LL | let _ = a != b || c != d; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:32:13 + --> $DIR/nonminimal_bool.rs:33:13 | LL | let _ = a != b && !(a != b && c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,5 +107,5 @@ error: this boolean expression can be simplified LL | if matches!(true, true) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)` -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors From 3514f2f2ab9a59306f75af9515435190ab3ccdbd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 17:04:22 +0200 Subject: [PATCH 552/806] Render niches on hover --- crates/hir/src/lib.rs | 13 +++++++++++-- crates/ide/src/hover/render.rs | 27 +++++++++++++++++++-------- crates/ide/src/hover/tests.rs | 6 +++--- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 38add4eda1ad0..f73c4f31eeee0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -62,7 +62,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding}, + layout::{LayoutError, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -133,8 +133,11 @@ pub use { }, hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + // FIXME: This just needs a HIR wrapper + layout::Layout, mir::MirEvalError, - PointerCast, Safety, + PointerCast, + Safety, }, }; @@ -4506,6 +4509,12 @@ impl HasCrate for Union { } } +impl HasCrate for Enum { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Field { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.parent_def(db).module(db).krate() diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 4cbe7cca5afe3..d4c310447f200 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Semantics, - TypeInfo, + db::HirDatabase, Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasCrate, + HasSource, HirDisplay, Layout, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -404,8 +404,9 @@ pub(super) fn definition( .map(|layout| format!(", offset = {:#X}", layout.fields.offset(id).bytes())), _ => None, }; + let niches = niches(db, it, &layout).unwrap_or_default(); Some(format!( - "size = {:#X}, align = {:#X}{}", + "size = {:#X}, align = {:#X}{}{niches}", layout.size.bytes(), layout.align.abi.bytes(), offset.as_deref().unwrap_or_default() @@ -415,8 +416,9 @@ pub(super) fn definition( Definition::Function(it) => label_and_docs(db, it), Definition::Adt(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.layout(db).ok()?; + let niches = niches(db, it, &layout).unwrap_or_default(); Some(format!( - "size = {:#X}, align = {:#X}", + "size = {:#X}, align = {:#X}{niches}", layout.size.bytes(), layout.align.abi.bytes() )) @@ -437,14 +439,15 @@ pub(super) fn definition( None } }, - |it| { + |&it| { let (layout, tag_size) = it.layout(db).ok()?; let size = layout.size.bytes_usize() - tag_size; if size == 0 { // There is no value in showing layout info for fieldless variants return None; } - Some(format!("size = {:#X}", layout.size.bytes())) + let niches = niches(db, it, &layout).unwrap_or_default(); + Some(format!("size = {:#X}{niches}", layout.size.bytes())) }, ), Definition::Const(it) => label_value_and_docs(db, it, |it| { @@ -473,10 +476,11 @@ pub(super) fn definition( Definition::TraitAlias(it) => label_and_docs(db, it), Definition::TypeAlias(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.ty(db).layout(db).ok()?; + let niches = niches(db, it, &layout).unwrap_or_default(); Some(format!( - "size = {:#X}, align = {:#X}", + "size = {:#X}, align = {:#X}{niches}", layout.size.bytes(), - layout.align.abi.bytes() + layout.align.abi.bytes(), )) }), Definition::BuiltinType(it) => { @@ -513,6 +517,13 @@ pub(super) fn definition( markup(docs, label, mod_path) } +fn niches(db: &RootDatabase, it: impl HasCrate, layout: &Layout) -> Option { + Some(format!( + ", niches = {}", + layout.largest_niche?.available(&*db.target_data_layout(it.krate(db).into())?) + )) +} + fn type_info( sema: &Semantics<'_, RootDatabase>, config: &HoverConfig, diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 4e171867fbff0..f8b5b654546f7 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1528,7 +1528,7 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - type foo = fn(a: i32, b: i32) -> i32 // size = 0x8, align = 0x8 + type foo = fn(a: i32, b: i32) -> i32 // size = 0x8, align = 0x8, niches = 1 ``` "#]], ); @@ -1546,7 +1546,7 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - type foo = fn(i32, i32) -> i32 // size = 0x8, align = 0x8 + type foo = fn(i32, i32) -> i32 // size = 0x8, align = 0x8, niches = 1 ``` "#]], ); @@ -1904,7 +1904,7 @@ fn test_hover_layout_of_enum() { ``` ```rust - enum Foo // size = 0x10, align = 0x8 + enum Foo // size = 0x10, align = 0x8, niches = 254 ``` "#]], ); From 1275adc2004625cb7650ce5a6e99034e50c153cd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 26 May 2023 17:18:27 +0200 Subject: [PATCH 553/806] Don't leak rustc Layout in hir layer --- crates/hir/src/lib.rs | 58 ++++++++++++++++++++++++---------- crates/ide/src/hover/render.rs | 40 +++++++---------------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f73c4f31eeee0..5c0320f733421 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -62,7 +62,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{LayoutError, RustcEnumVariantIdx, TagEncoding}, + layout::{Layout as TyLayout, LayoutError, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -133,11 +133,8 @@ pub use { }, hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, - // FIXME: This just needs a HIR wrapper - layout::Layout, mir::MirEvalError, - PointerCast, - Safety, + PointerCast, Safety, }, }; @@ -964,8 +961,8 @@ impl Field { Type::new(db, var_id, ty) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) + pub fn layout(&self, db: &dyn HirDatabase) -> Result { + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()).map(Layout) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -1138,10 +1135,10 @@ impl Enum { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<(Arc, usize), LayoutError> { + pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { let layout = Adt::from(self).layout(db)?; let tag_size = - if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.variants { + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.0.variants { match tag_encoding { TagEncoding::Direct => { let target_data_layout = db @@ -1222,11 +1219,11 @@ impl Variant { let parent_enum = self.parent_enum(db); let (parent_layout, tag_size) = parent_enum.layout(db)?; Ok(( - match &parent_layout.variants { + match &parent_layout.0.variants { layout::Variants::Multiple { variants, .. } => { - variants[RustcEnumVariantIdx(self.id)].clone() + Layout(Arc::new(variants[RustcEnumVariantIdx(self.id)].clone())) } - _ => (*parent_layout).clone(), + _ => parent_layout, }, tag_size, )) @@ -1258,11 +1255,11 @@ impl Adt { }) } - pub fn layout(self, db: &dyn HirDatabase) -> Result, LayoutError> { + pub fn layout(self, db: &dyn HirDatabase) -> Result { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner), self.krate(db).id) + db.layout_of_adt(self.into(), Substitution::empty(Interner), self.krate(db).id).map(Layout) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -4246,8 +4243,8 @@ impl Type { .collect() } - pub fn layout(&self, db: &dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty(self.ty.clone(), self.env.krate) + pub fn layout(&self, db: &dyn HirDatabase) -> Result { + db.layout_of_ty(self.ty.clone(), self.env.krate).map(Layout) } } @@ -4358,6 +4355,35 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option); + +impl Layout { + pub fn size(&self) -> u64 { + self.0.size.bytes() + } + + pub fn align(&self) -> u64 { + self.0.align.abi.bytes() + } + + pub fn niches(&self, db: &dyn HirDatabase, krate: Crate) -> Option { + Some(self.0.largest_niche?.available(&*db.target_data_layout(krate.id)?)) + } + + pub fn field_offset(&self, idx: usize) -> Option { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => Some(0), + layout::FieldsShape::Array { stride, count } => { + let i = u64::try_from(idx).ok()?; + (i < count).then_some((stride * i).bytes()) + } + layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BindingMode { Move, diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index d4c310447f200..96a97ab44c0f4 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use either::Either; use hir::{ - db::HirDatabase, Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasCrate, - HasSource, HirDisplay, Layout, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasCrate, HasSource, HirDisplay, + Layout, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -401,14 +401,14 @@ pub(super) fn definition( hir::VariantDef::Struct(s) => Adt::from(s) .layout(db) .ok() - .map(|layout| format!(", offset = {:#X}", layout.fields.offset(id).bytes())), + .and_then(|layout| Some(format!(", offset = {:#X}", layout.field_offset(id)?))), _ => None, }; let niches = niches(db, it, &layout).unwrap_or_default(); Some(format!( "size = {:#X}, align = {:#X}{}{niches}", - layout.size.bytes(), - layout.align.abi.bytes(), + layout.size(), + layout.align(), offset.as_deref().unwrap_or_default() )) }), @@ -417,11 +417,7 @@ pub(super) fn definition( Definition::Adt(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.layout(db).ok()?; let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!( - "size = {:#X}, align = {:#X}{niches}", - layout.size.bytes(), - layout.align.abi.bytes() - )) + Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align())) }), Definition::Variant(it) => label_value_and_layout_info_and_docs( db, @@ -441,13 +437,13 @@ pub(super) fn definition( }, |&it| { let (layout, tag_size) = it.layout(db).ok()?; - let size = layout.size.bytes_usize() - tag_size; + let size = layout.size() as usize - tag_size; if size == 0 { // There is no value in showing layout info for fieldless variants return None; } let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}{niches}", layout.size.bytes())) + Some(format!("size = {:#X}{niches}", layout.size())) }, ), Definition::Const(it) => label_value_and_docs(db, it, |it| { @@ -477,11 +473,7 @@ pub(super) fn definition( Definition::TypeAlias(it) => label_and_layout_info_and_docs(db, it, config, |&it| { let layout = it.ty(db).layout(db).ok()?; let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!( - "size = {:#X}, align = {:#X}{niches}", - layout.size.bytes(), - layout.align.abi.bytes(), - )) + Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align(),)) }), Definition::BuiltinType(it) => { return famous_defs @@ -518,10 +510,7 @@ pub(super) fn definition( } fn niches(db: &RootDatabase, it: impl HasCrate, layout: &Layout) -> Option { - Some(format!( - ", niches = {}", - layout.largest_niche?.available(&*db.target_data_layout(it.krate(db).into())?) - )) + Some(format!(", niches = {}", layout.niches(db, it.krate(db).into())?)) } fn type_info( @@ -571,7 +560,7 @@ fn closure_ty( let layout = if config.memory_layout { original .layout(sema.db) - .map(|x| format!(" // size = {}, align = {}", x.size.bytes(), x.align.abi.bytes())) + .map(|x| format!(" // size = {}, align = {}", x.size(), x.align())) .unwrap_or_default() } else { String::default() @@ -782,12 +771,7 @@ fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option Date: Tue, 30 May 2023 16:20:01 +0200 Subject: [PATCH 554/806] Add render configs for memory layout hovers --- crates/hir/src/lib.rs | 74 +++--- .../convert_named_struct_to_tuple_struct.rs | 3 +- .../convert_tuple_struct_to_named_struct.rs | 3 +- crates/ide-assists/src/tests.rs | 3 +- crates/ide/src/hover.rs | 17 +- crates/ide/src/hover/render.rs | 226 +++++++++++------- crates/ide/src/hover/tests.rs | 79 +++--- crates/ide/src/lib.rs | 5 +- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 57 ++++- docs/user/generated_config.adoc | 20 ++ editors/code/package.json | 74 ++++++ 12 files changed, 400 insertions(+), 163 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5c0320f733421..eee45b74d94dc 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, lang_item::LangItemTarget, - layout::{self, ReprOptions}, + layout::{self, ReprOptions, TargetDataLayout}, macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, @@ -62,7 +62,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{Layout as TyLayout, LayoutError, RustcEnumVariantIdx, TagEncoding}, + layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -133,6 +133,7 @@ pub use { }, hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + layout::LayoutError, mir::MirEvalError, PointerCast, Safety, }, @@ -962,7 +963,8 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()).map(Layout) + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -1135,23 +1137,8 @@ impl Enum { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { - let layout = Adt::from(self).layout(db)?; - let tag_size = - if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.0.variants { - match tag_encoding { - TagEncoding::Direct => { - let target_data_layout = db - .target_data_layout(self.module(db).krate().id) - .ok_or(LayoutError::TargetLayoutNotAvailable)?; - tag.size(&*target_data_layout).bytes_usize() - } - TagEncoding::Niche { .. } => 0, - } - } else { - 0 - }; - Ok((layout, tag_size)) + pub fn layout(self, db: &dyn HirDatabase) -> Result { + Adt::from(self).layout(db) } } @@ -1214,19 +1201,16 @@ impl Variant { db.const_eval_discriminant(self.into()) } - /// Return layout of the variant and tag size of the parent enum. - pub fn layout(&self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { + pub fn layout(&self, db: &dyn HirDatabase) -> Result { let parent_enum = self.parent_enum(db); - let (parent_layout, tag_size) = parent_enum.layout(db)?; - Ok(( - match &parent_layout.0.variants { - layout::Variants::Multiple { variants, .. } => { - Layout(Arc::new(variants[RustcEnumVariantIdx(self.id)].clone())) - } - _ => parent_layout, - }, - tag_size, - )) + let parent_layout = parent_enum.layout(db)?; + Ok(match &parent_layout.0.variants { + layout::Variants::Multiple { variants, .. } => Layout( + Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()), + db.target_data_layout(parent_enum.krate(db).into()).unwrap(), + ), + _ => parent_layout, + }) } } @@ -1259,7 +1243,9 @@ impl Adt { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner), self.krate(db).id).map(Layout) + let krate = self.krate(db).id; + db.layout_of_adt(self.into(), Substitution::empty(Interner), krate) + .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -4244,7 +4230,8 @@ impl Type { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty.clone(), self.env.krate).map(Layout) + db.layout_of_ty(self.ty.clone(), self.env.krate) + .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } } @@ -4356,7 +4343,7 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option); +pub struct Layout(Arc, Arc); impl Layout { pub fn size(&self) -> u64 { @@ -4367,8 +4354,8 @@ impl Layout { self.0.align.abi.bytes() } - pub fn niches(&self, db: &dyn HirDatabase, krate: Crate) -> Option { - Some(self.0.largest_niche?.available(&*db.target_data_layout(krate.id)?)) + pub fn niches(&self) -> Option { + Some(self.0.largest_niche?.available(&*self.1)) } pub fn field_offset(&self, idx: usize) -> Option { @@ -4382,6 +4369,19 @@ impl Layout { layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), } } + + pub fn enum_tag_size(&self) -> Option { + let tag_size = + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { + match tag_encoding { + TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Niche { .. } => 0, + } + } else { + return None; + }; + Some(tag_size) + } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 00a4e0530d2b9..fe1cb6fce3630 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -55,7 +55,8 @@ pub(crate) fn convert_named_struct_to_tuple_struct( // XXX: We don't currently provide this assist for struct definitions inside macros, but if we // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files // too. - let strukt = ctx.find_node_at_offset::>()?; + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let record_fields = match field_list { ast::FieldList::RecordFieldList(it) => it, diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 772e032fb2934..017853a4a2023 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -50,7 +50,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let strukt = ctx.find_node_at_offset::>()?; + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let tuple_fields = match field_list { ast::FieldList::TupleFieldList(it) => it, diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 6e161ca43969d..344f2bfcce14a 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -273,8 +273,9 @@ fn assist_order_field_struct() { assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); - assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct"); assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); + assert_eq!(assists.next().expect("expected assist").label, "Generate `new`"); + assert_eq!(assists.next().map(|it| it.label.to_string()), None); } #[test] diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 1a84a963f55b6..5ef6ac9480721 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -27,12 +27,27 @@ use crate::{ #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { pub links_in_hover: bool, - pub memory_layout: bool, + pub memory_layout: Option, pub documentation: bool, pub keywords: bool, pub format: HoverDocFormat, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct MemoryLayoutHoverConfig { + pub size: Option, + pub offset: Option, + pub alignment: Option, + pub niches: bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MemoryLayoutHoverRenderKind { + Decimal, + Hexadecimal, + Both, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum HoverDocFormat { Markdown, diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 96a97ab44c0f4..1362146413e0d 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasCrate, HasSource, HirDisplay, - Layout, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout, + LayoutError, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -27,7 +27,8 @@ use syntax::{ use crate::{ doc_links::{remove_links, rewrite_links}, hover::walk_and_push_ty, - HoverAction, HoverConfig, HoverResult, Markup, + HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, + MemoryLayoutHoverRenderKind, }; pub(super) fn type_info_of( @@ -393,32 +394,27 @@ pub(super) fn definition( let mod_path = definition_mod_path(db, &def); let (label, docs) = match def { Definition::Macro(it) => label_and_docs(db, it), - Definition::Field(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let var_def = it.parent_def(db); - let id = it.index(); - let layout = it.layout(db).ok()?; - let offset = match var_def { - hir::VariantDef::Struct(s) => Adt::from(s) - .layout(db) - .ok() - .and_then(|layout| Some(format!(", offset = {:#X}", layout.field_offset(id)?))), - _ => None, - }; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!( - "size = {:#X}, align = {:#X}{}{niches}", - layout.size(), - layout.align(), - offset.as_deref().unwrap_or_default() - )) - }), + Definition::Field(it) => label_and_layout_info_and_docs( + db, + it, + config, + |&it| it.layout(db), + |_| { + let var_def = it.parent_def(db); + let id = it.index(); + match var_def { + hir::VariantDef::Struct(s) => { + Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id)) + } + _ => None, + } + }, + ), Definition::Module(it) => label_and_docs(db, it), Definition::Function(it) => label_and_docs(db, it), - Definition::Adt(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let layout = it.layout(db).ok()?; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align())) - }), + Definition::Adt(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None) + } Definition::Variant(it) => label_value_and_layout_info_and_docs( db, it, @@ -435,16 +431,8 @@ pub(super) fn definition( None } }, - |&it| { - let (layout, tag_size) = it.layout(db).ok()?; - let size = layout.size() as usize - tag_size; - if size == 0 { - // There is no value in showing layout info for fieldless variants - return None; - } - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}{niches}", layout.size())) - }, + |it| it.layout(db), + |layout| layout.enum_tag_size(), ), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); @@ -470,11 +458,9 @@ pub(super) fn definition( }), Definition::Trait(it) => label_and_docs(db, it), Definition::TraitAlias(it) => label_and_docs(db, it), - Definition::TypeAlias(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let layout = it.ty(db).layout(db).ok()?; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align(),)) - }), + Definition::TypeAlias(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None) + } Definition::BuiltinType(it) => { return famous_defs .and_then(|fd| builtin(fd, it)) @@ -509,10 +495,6 @@ pub(super) fn definition( markup(docs, label, mod_path) } -fn niches(db: &RootDatabase, it: impl HasCrate, layout: &Layout) -> Option { - Some(format!(", niches = {}", layout.niches(db, it.krate(db).into())?)) -} - fn type_info( sema: &Semantics<'_, RootDatabase>, config: &HoverConfig, @@ -557,14 +539,6 @@ fn closure_ty( TypeInfo { original, adjusted }: &TypeInfo, ) -> Option { let c = original.as_closure()?; - let layout = if config.memory_layout { - original - .layout(sema.db) - .map(|x| format!(" // size = {}, align = {}", x.size(), x.align())) - .unwrap_or_default() - } else { - String::default() - }; let mut captures_rendered = c.captured_items(sema.db) .into_iter() .map(|it| { @@ -600,17 +574,23 @@ fn closure_ty( } else { String::new() }; + let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),); - let mut res = HoverResult::default(); - res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); - res.markup = format!( - "```rust\n{}{}\n{}\n```{adjusted}\n\n## Captures\n{}", - c.display_with_id(sema.db), - layout, + if let Some(layout) = + render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) + { + format_to!(markup, "{layout}"); + } + format_to!( + markup, + "\n{}\n```{adjusted}\n\n## Captures\n{}", c.display_with_impl(sema.db), captures_rendered, - ) - .into(); + ); + + let mut res = HoverResult::default(); + res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); + res.markup = markup.into(); Some(res) } @@ -644,48 +624,59 @@ where (label, docs) } -fn label_and_layout_info_and_docs( +fn label_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, layout_extractor: E, + layout_offset_extractor: E2, ) -> (String, Option) where D: HasAttrs + HirDisplay, - E: Fn(&D) -> Option, - V: Display, + E: Fn(&D) -> Result, + E2: Fn(&Layout) -> Option, { - let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { - Some(layout) => format!("{} // {layout}", def.display(db)), - _ => def.display(db).to_string(), - }; + let mut label = def.display(db).to_string(); + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + layout_offset_extractor, + |_| None, + ) { + format_to!(label, "{layout}"); + } let docs = def.attrs(db).docs(); (label, docs) } -fn label_value_and_layout_info_and_docs( +fn label_value_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, value_extractor: E, layout_extractor: E2, + layout_tag_extractor: E3, ) -> (String, Option) where D: HasAttrs + HirDisplay, E: Fn(&D) -> Option, - E2: Fn(&D) -> Option, + E2: Fn(&D) -> Result, + E3: Fn(&Layout) -> Option, V: Display, - L: Display, { let value = value_extractor(&def); - let label = match value { + let mut label = match value { Some(value) => format!("{} = {value}", def.display(db)), None => def.display(db).to_string(), }; - let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { - Some(layout) => format!("{} // {layout}", label), - _ => label, - }; + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + |_| None, + layout_tag_extractor, + ) { + format_to!(label, "{layout}"); + } let docs = def.attrs(db).docs(); (label, docs) } @@ -769,14 +760,87 @@ fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option format!("{is_mut}self: {ty}"), }; - if config.memory_layout { - if let Ok(layout) = it.ty(db).layout(db) { - format_to!(desc, " // size = {}, align = {}", layout.size(), layout.align()); - } + if let Some(layout) = + render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) + { + format_to!(desc, "{layout}"); } markup(None, desc, None) } +fn render_memory_layout( + config: Option, + layout: impl FnOnce() -> Result, + offset: impl FnOnce(&Layout) -> Option, + tag: impl FnOnce(&Layout) -> Option, +) -> Option { + // field + + let config = config?; + let layout = layout().ok()?; + + let mut label = String::from(" // "); + + if let Some(render) = config.size { + let size = match tag(&layout) { + Some(tag) => layout.size() as usize - tag, + None => layout.size() as usize, + }; + format_to!(label, "size = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"), + MemoryLayoutHoverRenderKind::Both if size >= 10 => { + format_to!(label, "{size} ({size:#X})") + } + MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"), + } + format_to!(label, ", "); + } + + if let Some(render) = config.alignment { + let align = layout.align(); + format_to!(label, "align = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",), + MemoryLayoutHoverRenderKind::Both if align >= 10 => { + format_to!(label, "{align} ({align:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{align}") + } + } + format_to!(label, ", "); + } + + if let Some(render) = config.offset { + if let Some(offset) = offset(&layout) { + format_to!(label, "offset = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"), + MemoryLayoutHoverRenderKind::Both if offset >= 10 => { + format_to!(label, "{offset} ({offset:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{offset}") + } + } + format_to!(label, ", "); + } + } + + if config.niches { + if let Some(niches) = layout.niches() { + format_to!(label, "niches = {niches}, "); + } + } + label.pop(); // ' ' + label.pop(); // ',' + Some(label) +} + struct KeywordHint { description: String, keyword_mod: String, diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f8b5b654546f7..f2ee79a23e6f3 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -2,11 +2,18 @@ use expect_test::{expect, Expect}; use ide_db::base_db::{FileLoader, FileRange}; use syntax::TextRange; -use crate::{fixture, HoverConfig, HoverDocFormat}; +use crate::{ + fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, +}; const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, - memory_layout: true, + memory_layout: Some(MemoryLayoutHoverConfig { + size: Some(MemoryLayoutHoverRenderKind::Both), + offset: Some(MemoryLayoutHoverRenderKind::Both), + alignment: Some(MemoryLayoutHoverRenderKind::Both), + niches: true, + }), documentation: true, format: HoverDocFormat::Markdown, keywords: true, @@ -62,7 +69,7 @@ fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { memory_layout: false, ..HOVER_BASE_CONFIG }, + &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -237,7 +244,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl Fn(i32) -> i32 ``` @@ -292,7 +299,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 16, align = 8 + {closure#0} // size = 16 (0x10), align = 8, niches = 1 impl FnOnce() ``` @@ -320,7 +327,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl FnMut() ``` @@ -344,7 +351,7 @@ fn main() { "#, expect![[r#" ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl FnOnce() -> S2 ``` Coerced to: &impl FnOnce() -> S2 @@ -667,7 +674,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 0x1, align = 0x1, offset = 0x4 + field_a: u8 // size = 1, align = 1, offset = 4 ``` "#]], ); @@ -692,7 +699,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 + field_a: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -714,7 +721,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 + field_a: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -1528,7 +1535,7 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - type foo = fn(a: i32, b: i32) -> i32 // size = 0x8, align = 0x8, niches = 1 + type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -1546,7 +1553,7 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - type foo = fn(i32, i32) -> i32 // size = 0x8, align = 0x8, niches = 1 + type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -1674,7 +1681,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1710,7 +1717,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1739,7 +1746,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1767,7 +1774,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0x0, align = 0x1 + pub struct Bar // size = 0, align = 1 ``` --- @@ -1794,7 +1801,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0x0, align = 0x1 + pub struct Bar // size = 0, align = 1 ``` --- @@ -1883,7 +1890,7 @@ fn test_hover_layout_of_variant() { ``` ```rust - Variant1(u8, u16) // size = 0x4 + Variant1(u8, u16) // size = 4, align = 2 ``` "#]], ); @@ -1904,7 +1911,7 @@ fn test_hover_layout_of_enum() { ``` ```rust - enum Foo // size = 0x10, align = 0x8, niches = 254 + enum Foo // size = 16 (0x10), align = 8, niches = 254 ``` "#]], ); @@ -3204,7 +3211,7 @@ fn main() { *f* ```rust - f: &i32 // size = 8, align = 8 + f: &i32 // size = 8, align = 8, niches = 1 ``` --- @@ -3213,7 +3220,7 @@ fn main() { ``` ```rust - f: i32 // size = 0x4, align = 0x4, offset = 0x0 + f: i32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -3353,7 +3360,7 @@ impl Foo { *self* ```rust - self: &Foo // size = 8, align = 8 + self: &Foo // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -3758,7 +3765,7 @@ type Fo$0o2 = Foo<2>; ``` ```rust - type Foo2 = Foo<2> // size = 0x0, align = 0x1 + type Foo2 = Foo<2> // size = 0, align = 1 ``` "#]], ); @@ -3800,7 +3807,7 @@ enum E { ``` ```rust - A = 8 + A = 8 // size = 1, align = 1 ``` --- @@ -3825,7 +3832,7 @@ enum E { ``` ```rust - A = 12 (0xC) + A = 12 (0xC) // size = 1, align = 1 ``` --- @@ -3851,7 +3858,7 @@ enum E { ``` ```rust - B = 2 + B = 2 // size = 1, align = 1 ``` --- @@ -3877,7 +3884,7 @@ enum E { ``` ```rust - B = 5 + B = 5 // size = 1, align = 1 ``` --- @@ -4411,7 +4418,7 @@ fn foo(e: E) { ``` ```rust - A = 3 + A = 3 // size = 0, align = 1 ``` --- @@ -4433,7 +4440,7 @@ fn main() { *tile4* ```rust - let tile4: [u32; 8] // size = 32, align = 4 + let tile4: [u32; 8] // size = 32 (0x20), align = 4 ``` "#]], ); @@ -4669,7 +4676,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - pub struct TheItem // size = 0x0, align = 0x1 + pub struct TheItem // size = 0, align = 1 ``` --- @@ -4817,7 +4824,7 @@ mod string { ``` ```rust - struct String // size = 0x0, align = 0x1 + struct String // size = 0, align = 1 ``` --- @@ -5486,7 +5493,7 @@ foo_macro!( ``` ```rust - pub struct Foo // size = 0x0, align = 0x1 + pub struct Foo // size = 0, align = 1 ``` --- @@ -5511,7 +5518,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo // size = 0x4, align = 0x4 + pub struct Foo // size = 4, align = 4 ``` --- @@ -5610,7 +5617,7 @@ enum Enum { ``` ```rust - RecordV { field: u32 } // size = 0x4 + RecordV { field: u32 } // size = 4, align = 4 ``` "#]], ); @@ -5632,7 +5639,7 @@ enum Enum { ``` ```rust - field: u32 // size = 0x4, align = 0x4 + field: u32 // size = 4, align = 4 ``` "#]], ); @@ -6134,7 +6141,7 @@ fn test() { ``` ```rust - f: u32 // size = 0x4, align = 0x4, offset = 0x0 + f: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index c02dbc60a339b..87e769e423073 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -84,7 +84,10 @@ pub use crate::{ file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, - hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, + hover::{ + HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, + MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + }, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index e7b223caab43d..3e3d9f8f85c45 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -138,7 +138,7 @@ impl StaticIndex<'_> { }); let hover_config = HoverConfig { links_in_hover: true, - memory_layout: true, + memory_layout: None, documentation: true, keywords: true, format: crate::HoverDocFormat::Markdown, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 649ac90fc6e07..6355c620f7802 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -14,7 +14,7 @@ use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, - JoinLinesConfig, Snippet, SnippetScope, + JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -317,8 +317,16 @@ config_data! { hover_documentation_keywords_enable: bool = "true", /// Use markdown syntax for links on hover. hover_links_enable: bool = "true", + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = "\"hexadecimal\"", /// Whether to show memory layout data on hover. hover_memoryLayout_enable: bool = "true", + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = "false", + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = "\"hexadecimal\"", + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = "\"both\"", /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = "false", @@ -1514,9 +1522,19 @@ impl Config { } pub fn hover(&self) -> HoverConfig { + let mem_kind = |kind| match kind { + MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both, + MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal, + MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal, + }; HoverConfig { links_in_hover: self.data.hover_links_enable, - memory_layout: self.data.hover_memoryLayout_enable, + memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig { + size: self.data.hover_memoryLayout_size.map(mem_kind), + offset: self.data.hover_memoryLayout_offset.map(mem_kind), + alignment: self.data.hover_memoryLayout_alignment.map(mem_kind), + niches: self.data.hover_memoryLayout_niches.unwrap_or_default(), + }), documentation: self.data.hover_documentation_enable, format: { let is_markdown = try_or_def!(self @@ -1726,6 +1744,9 @@ mod de_unit_v { named_unit_variant!(reborrow); named_unit_variant!(fieldless); named_unit_variant!(with_block); + named_unit_variant!(decimal); + named_unit_variant!(hexadecimal); + named_unit_variant!(both); } #[derive(Deserialize, Debug, Clone, Copy)] @@ -1956,6 +1977,18 @@ enum WorkspaceSymbolSearchKindDef { AllSymbols, } +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +#[serde(untagged)] +pub enum MemoryLayoutHoverRenderKindDef { + #[serde(deserialize_with = "de_unit_v::decimal")] + Decimal, + #[serde(deserialize_with = "de_unit_v::hexadecimal")] + Hexadecimal, + #[serde(deserialize_with = "de_unit_v::both")] + Both, +} + macro_rules! _config_data { (struct $name:ident { $( @@ -2038,7 +2071,9 @@ fn get_field( None } }) - .unwrap_or_else(|| serde_json::from_str(default).unwrap()) + .unwrap_or_else(|| { + serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`")) + }) } fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value { @@ -2366,6 +2401,22 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "`hide`: Shows `...` for every closure type", ], }, + "Option" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": ["both", "decimal", "hexadecimal", ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ], + }, + ], + }, _ => panic!("missing entry for {ty}: {default}"), } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index dc97366eef1aa..ea00c9540ff14 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -428,11 +428,31 @@ Whether to show keyword hover popups. Only applies when -- Use markdown syntax for links on hover. -- +[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: ++ +-- +How to render the align information in a memory layout hover. +-- [[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`):: + -- Whether to show memory layout data on hover. -- +[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`):: ++ +-- +How to render the niche information in a memory layout hover. +-- +[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`):: ++ +-- +How to render the offset information in a memory layout hover. +-- +[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`):: ++ +-- +How to render the size information in a memory layout hover. +-- [[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index aa63c40c0d299..ee1f832d323f7 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -966,11 +966,85 @@ "default": true, "type": "boolean" }, + "rust-analyzer.hover.memoryLayout.alignment": { + "markdownDescription": "How to render the align information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, "rust-analyzer.hover.memoryLayout.enable": { "markdownDescription": "Whether to show memory layout data on hover.", "default": true, "type": "boolean" }, + "rust-analyzer.hover.memoryLayout.niches": { + "markdownDescription": "How to render the niche information in a memory layout hover.", + "default": false, + "type": [ + "null", + "boolean" + ] + }, + "rust-analyzer.hover.memoryLayout.offset": { + "markdownDescription": "How to render the offset information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, + "rust-analyzer.hover.memoryLayout.size": { + "markdownDescription": "How to render the size information in a memory layout hover.", + "default": "both", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, "rust-analyzer.imports.granularity.enforce": { "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", "default": false, From 288312463e2dbfac15b5ba2dbbc2d015d5dfb683 Mon Sep 17 00:00:00 2001 From: Charalampos Mitrodimas Date: Tue, 30 May 2023 21:29:04 +0300 Subject: [PATCH 555/806] [`wildcard_imports`] Modules that contain `prelude` are also allowed This commit fixes #10846 by checking if the path segment contains the word "prelude". Signed-off-by: Charalampos Mitrodimas --- clippy_lints/src/wildcard_imports.rs | 9 ++++-- tests/ui/auxiliary/wildcard_imports_helper.rs | 6 ++++ tests/ui/wildcard_imports.fixed | 2 ++ tests/ui/wildcard_imports.rs | 2 ++ tests/ui/wildcard_imports.stderr | 30 +++++++++---------- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index a9089fba3c539..b6e4cd22789f9 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -65,8 +65,9 @@ declare_clippy_lint! { /// This can lead to confusing error messages at best and to unexpected behavior at worst. /// /// ### Exceptions - /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) - /// provide modules named "prelude" specifically designed for wildcard import. + /// Wildcard imports are allowed from modules that their name contains `prelude`. Many crates + /// (including the standard library) provide modules named "prelude" specifically designed + /// for wildcard import. /// /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// @@ -212,7 +213,9 @@ impl WildcardImports { // Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().any(|ps| ps.ident.name == sym::prelude) + segments + .iter() + .any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str())) } // Allow "super::*" imports in tests. diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs index d75cdd625f9ec..44f49c080cdd6 100644 --- a/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -25,3 +25,9 @@ pub mod prelude { pub struct PreludeModAnywhere; } } + +pub mod extern_prelude { + pub mod v1 { + pub struct ExternPreludeModAnywhere; + } +} diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index bd845361fa89d..733bbcfbcefab 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::extern_prelude::v1::*; use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -81,6 +82,7 @@ fn main() { let _ = inner_struct_mod::C; let _ = ExternA; let _ = PreludeModAnywhere; + let _ = ExternPreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index fb51f7bdfcc6b..4acdd374bdeca 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::extern_prelude::v1::*; use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -81,6 +82,7 @@ fn main() { let _ = inner_struct_mod::C; let _ = ExternA; let _ = PreludeModAnywhere; + let _ = ExternPreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 6b469cdfc4449..235be2d57081b 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:13 + --> $DIR/wildcard_imports.rs:97:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:101:75 + --> $DIR/wildcard_imports.rs:103:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:13 + --> $DIR/wildcard_imports.rs:104:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:113:20 + --> $DIR/wildcard_imports.rs:115:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:113:30 + --> $DIR/wildcard_imports.rs:115:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:120:13 + --> $DIR/wildcard_imports.rs:122:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:149:9 + --> $DIR/wildcard_imports.rs:151:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:158:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:161:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:170:13 + --> $DIR/wildcard_imports.rs:172:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:205:17 + --> $DIR/wildcard_imports.rs:207:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:213:13 + --> $DIR/wildcard_imports.rs:215:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:222:17 + --> $DIR/wildcard_imports.rs:224:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:231:13 + --> $DIR/wildcard_imports.rs:233:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:239:13 + --> $DIR/wildcard_imports.rs:241:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` From e4927f98fa53a9d4dd466a9035f71fb1c2a5940b Mon Sep 17 00:00:00 2001 From: disco07 Date: Wed, 31 May 2023 00:17:26 +0200 Subject: [PATCH 556/806] change booleans file and update tests --- clippy_lints/src/booleans.rs | 32 +++----------------------------- tests/ui/nonminimal_bool.rs | 2 +- tests/ui/nonminimal_bool.stderr | 26 ++++++++++++++++---------- 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 4bd48185f37e5..ff0102255a0a9 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -8,12 +8,10 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; -use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; use rustc_span::sym; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does @@ -90,28 +88,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { NonminimalBoolVisitor { cx }.visit_body(body); } } - -fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - cx.tcx - .lang_items() - .not_trait() - .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) - .and_then(|trait_id| { - cx.tcx.associated_items(trait_id).find_by_name_and_kind( - cx.tcx, - Ident::from_str("Output"), - ty::AssocKind::Type, - trait_id, - ) - }) - .map_or(false, |assoc_item| { - let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, [])); - let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); - - nty.is_bool() - }) -} - struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } @@ -496,11 +472,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { self.bool_expr(e); }, ExprKind::Unary(UnOp::Not, inner) => { - if let ExprKind::Unary(UnOp::Not, ex) = inner.kind { - let ty = self.cx.typeck_results().expr_ty(ex); - if is_impl_not_trait_with_bool_out(self.cx, ty) { - return; - } + if let ExprKind::Unary(UnOp::Not, ex) = inner.kind && + !self.cx.typeck_results().node_types()[ex.hir_id].is_bool() { + return; } if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() { self.bool_expr(e); diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 54cbbdef22e0f..fec6b7713eef8 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -10,7 +10,6 @@ fn main() { let e: bool = unimplemented!(); let _ = !true; let _ = !false; - // vvv Should not lint let _ = !!a; let _ = false || a; // don't lint on cfgs @@ -55,6 +54,7 @@ fn issue4548() { fn check_expect() { let a: bool = unimplemented!(); + #[expect(clippy::nonminimal_bool)] let _ = !!a; } diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index fa14471301ee1..91b5805aa97ab 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -13,31 +13,37 @@ LL | let _ = !false; | ^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:15:13 + --> $DIR/nonminimal_bool.rs:13:13 + | +LL | let _ = !!a; + | ^^^ help: try: `a` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:14:13 | LL | let _ = false || a; | ^^^^^^^^^^ help: try: `a` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:19:13 + --> $DIR/nonminimal_bool.rs:18:13 | LL | let _ = !(!a && b); | ^^^^^^^^^^ help: try: `a || !b` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:20:13 + --> $DIR/nonminimal_bool.rs:19:13 | LL | let _ = !(!a || b); | ^^^^^^^^^^ help: try: `a && !b` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:21:13 + --> $DIR/nonminimal_bool.rs:20:13 | LL | let _ = !a && !(b && c); | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:29:13 + --> $DIR/nonminimal_bool.rs:28:13 | LL | let _ = a == b && c == 5 && a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +56,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:30:13 + --> $DIR/nonminimal_bool.rs:29:13 | LL | let _ = a == b || c == 5 || a == b; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +69,7 @@ LL | let _ = a == b || c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:31:13 + --> $DIR/nonminimal_bool.rs:30:13 | LL | let _ = a == b && c == 5 && b == a; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,7 +82,7 @@ LL | let _ = a == b && c == 5; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:32:13 + --> $DIR/nonminimal_bool.rs:31:13 | LL | let _ = a != b || !(a != b || c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +95,7 @@ LL | let _ = a != b || c != d; | ~~~~~~~~~~~~~~~~ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool.rs:33:13 + --> $DIR/nonminimal_bool.rs:32:13 | LL | let _ = a != b && !(a != b && c == d); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,5 +113,5 @@ error: this boolean expression can be simplified LL | if matches!(true, true) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From 2f0a26607f324ad2d630c39e718e55725f8b108d Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 20 May 2023 10:40:30 -0500 Subject: [PATCH 557/806] Make `x test --dry-run` less verbose Previously, this would print a message for each doctest, which was quite verbose: ``` doc tests for: /home/jyn/src/rust/src/doc/rustc/src/exploit-mitigations.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/instrument-coverage.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/json.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/linker-plugin-lto.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/lints/groups.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/lints/index.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/lints/levels.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/aarch64-nintendo-switch-freestanding.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/apple-watchos.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv4t-none-eabi.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv5te-none-eabi.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/esp-idf.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/fuchsia.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/kmc-solid.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/loongarch-linux.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/mipsel-sony-psx.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/nto-qnx.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/openbsd.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/openharmony.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/riscv32imac-unknown-xous-elf.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/unknown-uefi.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/platform-support/x86_64-unknown-none.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/profile-guided-optimization.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/target-tier-policy.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/targets/custom.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/targets/index.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/tests/index.md doc tests for: /home/jyn/src/rust/src/doc/rustc/src/what-is-rustc.md ``` --- src/bootstrap/test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 960abb31b2016..9f365474a70c8 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1776,6 +1776,8 @@ impl Step for BookTest { /// /// This uses the `rustdoc` that sits next to `compiler`. fn run(self, builder: &Builder<'_>) { + let host = self.compiler.host; + let _guard = builder.msg(Kind::Test, self.compiler.stage, &format!("book {}", self.name), host, host); // External docs are different from local because: // - Some books need pre-processing by mdbook before being tested. // - They need to save their state to toolstate. @@ -1967,7 +1969,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> } } - builder.info(&format!("doc tests for: {}", markdown.display())); + builder.verbose(&format!("doc tests for: {}", markdown.display())); let mut cmd = builder.rustdoc_cmd(compiler); builder.add_rust_test_threads(&mut cmd); // allow for unstable options such as new editions From a80d69a6ac996616937fb41357f10db036f85f73 Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 22 Apr 2023 09:53:22 -0500 Subject: [PATCH 558/806] Fix `x test --stage 2 core` when download-rustc is enabled This works by building std from source unconditionally instead of downloading it, for library tests only. This was somewhat complicated because of the following requirements: 1. Unconditionally downloading libstd breaks `x test std`, because `coretests` requires the std loaded from the sysroot to match the std that's currently being tested. 2. Unconditionally rebuilding libstd breaks `x test ui-fulldeps librustdoc`, because anything loading `rustc_private` needs to use the same libstd that rustc was built with. Break the knot by introducing a new `stage2-test-sysroot`, used only for testing `std` itself. This holds a freshly compiled std, while `stage2` and `ci-rustc-sysroot` still hold the downloaded std. This also extends the existing `cp_filtered` in Sysroot to apply to the `rust-std` component, not just the `rustc-dev` component. --- src/bootstrap/builder.rs | 2 +- src/bootstrap/compile.rs | 120 +++++++++++++++++++++++++++++--------- src/bootstrap/download.rs | 33 ++++++++--- src/bootstrap/test.rs | 34 ++++++++++- 4 files changed, 149 insertions(+), 40 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 30359e47e73be..b69f99c5e7806 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -992,7 +992,7 @@ impl<'a> Builder<'a> { } pub fn sysroot(&self, compiler: Compiler) -> Interned { - self.ensure(compile::Sysroot { compiler }) + self.ensure(compile::Sysroot::new(compiler)) } /// Returns the libdir where the standard library and other artifacts are diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index a7ebd018a8791..7c09d2043f116 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -40,11 +40,18 @@ pub struct Std { /// /// This shouldn't be used from other steps; see the comment on [`Rustc`]. crates: Interned>, + /// When using download-rustc, we need to use a new build of `std` for running unit tests of Std itself, + /// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overriden by `builder.ensure` from other steps. + force_recompile: bool, } impl Std { pub fn new(compiler: Compiler, target: TargetSelection) -> Self { - Self { target, compiler, crates: Default::default() } + Self { target, compiler, crates: Default::default(), force_recompile: false } + } + + pub fn force_recompile(compiler: Compiler, target: TargetSelection) -> Self { + Self { target, compiler, crates: Default::default(), force_recompile: true } } } @@ -77,6 +84,7 @@ impl Step for Std { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, crates: make_run_crates(&run, "library"), + force_recompile: false, }); } @@ -89,11 +97,20 @@ impl Step for Std { let target = self.target; let compiler = self.compiler; - // When using `download-rustc`, we already have artifacts for the host available - // (they were copied in `impl Step for Sysroot`). Don't recompile them. - // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, - // so its artifacts can't be reused. - if builder.download_rustc() && compiler.stage != 0 && target == builder.build.build { + // When using `download-rustc`, we already have artifacts for the host available. Don't + // recompile them. + if builder.download_rustc() && target == builder.build.build + // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so + // its artifacts can't be reused. + && compiler.stage != 0 + // This check is specific to testing std itself; see `test::Std` for more details. + && !self.force_recompile + { + cp_rustc_component_to_ci_sysroot( + builder, + compiler, + builder.config.ci_rust_std_contents(), + ); return; } @@ -428,6 +445,8 @@ struct StdLink { pub target: TargetSelection, /// Not actually used; only present to make sure the cache invalidation is correct. crates: Interned>, + /// See [`Std::force_recompile`]. + force_recompile: bool, } impl StdLink { @@ -437,6 +456,7 @@ impl StdLink { target_compiler: std.compiler, target: std.target, crates: std.crates, + force_recompile: std.force_recompile, } } } @@ -460,8 +480,24 @@ impl Step for StdLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - let libdir = builder.sysroot_libdir(target_compiler, target); - let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); + + // NOTE: intentionally does *not* check `target == builder.build` to avoid having to add the same check in `test::Crate`. + let (libdir, hostdir) = if self.force_recompile && builder.download_rustc() { + // NOTE: copies part of `sysroot_libdir` to avoid having to add a new `force_recompile` argument there too + let lib = builder.sysroot_libdir_relative(self.compiler); + let sysroot = builder.ensure(crate::compile::Sysroot { + compiler: self.compiler, + force_recompile: self.force_recompile, + }); + let libdir = sysroot.join(lib).join("rustlib").join(target.triple).join("lib"); + let hostdir = sysroot.join(lib).join("rustlib").join(compiler.host.triple).join("lib"); + (INTERNER.intern_path(libdir), INTERNER.intern_path(hostdir)) + } else { + let libdir = builder.sysroot_libdir(target_compiler, target); + let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); + (libdir, hostdir) + }; + add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); } } @@ -594,6 +630,25 @@ impl Step for StartupObjects { } } +fn cp_rustc_component_to_ci_sysroot( + builder: &Builder<'_>, + compiler: Compiler, + contents: Vec, +) { + let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false }); + + let ci_rustc_dir = builder.out.join(&*builder.build.build.triple).join("ci-rustc"); + for file in contents { + let src = ci_rustc_dir.join(&file); + let dst = sysroot.join(file); + if src.is_dir() { + t!(fs::create_dir_all(dst)); + } else { + builder.copy(&src, &dst); + } + } +} + #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, @@ -653,18 +708,11 @@ impl Step for Rustc { if builder.download_rustc() && compiler.stage != 0 { // Copy the existing artifacts instead of rebuilding them. // NOTE: this path is only taken for tools linking to rustc-dev (including ui-fulldeps tests). - let sysroot = builder.ensure(Sysroot { compiler }); - - let ci_rustc_dir = builder.out.join(&*builder.build.build.triple).join("ci-rustc"); - for file in builder.config.rustc_dev_contents() { - let src = ci_rustc_dir.join(&file); - let dst = sysroot.join(file); - if src.is_dir() { - t!(fs::create_dir_all(dst)); - } else { - builder.copy(&src, &dst); - } - } + cp_rustc_component_to_ci_sysroot( + builder, + compiler, + builder.config.ci_rustc_dev_contents(), + ); return; } @@ -1225,6 +1273,14 @@ pub fn compiler_file( #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Sysroot { pub compiler: Compiler, + /// See [`Std::force_recompile`]. + force_recompile: bool, +} + +impl Sysroot { + pub(crate) fn new(compiler: Compiler) -> Self { + Sysroot { compiler, force_recompile: false } + } } impl Step for Sysroot { @@ -1247,6 +1303,8 @@ impl Step for Sysroot { let sysroot_dir = |stage| { if stage == 0 { host_dir.join("stage0-sysroot") + } else if self.force_recompile && stage == compiler.stage { + host_dir.join(format!("stage{stage}-test-sysroot")) } else if builder.download_rustc() && compiler.stage != builder.top_stage { host_dir.join("ci-rustc-sysroot") } else { @@ -1286,14 +1344,19 @@ impl Step for Sysroot { // 2. The sysroot is deleted and recreated between each invocation, so running `x test // ui-fulldeps && x test ui` can't cause failures. let mut filtered_files = Vec::new(); - // Don't trim directories or files that aren't loaded per-target; they can't cause conflicts. - let suffix = format!("lib/rustlib/{}/lib", compiler.host); - for path in builder.config.rustc_dev_contents() { - let path = Path::new(&path); - if path.parent().map_or(false, |parent| parent.ends_with(&suffix)) { - filtered_files.push(path.file_name().unwrap().to_owned()); + let mut add_filtered_files = |suffix, contents| { + for path in contents { + let path = Path::new(&path); + if path.parent().map_or(false, |parent| parent.ends_with(&suffix)) { + filtered_files.push(path.file_name().unwrap().to_owned()); + } } - } + }; + let suffix = format!("lib/rustlib/{}/lib", compiler.host); + add_filtered_files(suffix.as_str(), builder.config.ci_rustc_dev_contents()); + // NOTE: we can't copy std eagerly because `stage2-test-sysroot` needs to have only the + // newly compiled std, not the downloaded std. + add_filtered_files("lib", builder.config.ci_rust_std_contents()); let filtered_extensions = [OsStr::new("rmeta"), OsStr::new("rlib"), OsStr::new("so")]; let ci_rustc_dir = builder.ci_rustc_dir(builder.config.build); @@ -1411,7 +1474,8 @@ impl Step for Assemble { // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. if builder.download_rustc() { - let sysroot = builder.ensure(Sysroot { compiler: target_compiler }); + let sysroot = + builder.ensure(Sysroot { compiler: target_compiler, force_recompile: false }); // Ensure that `libLLVM.so` ends up in the newly created target directory, // so that tools using `rustc_private` can use it. dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot); diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index c7969d2a2c7be..c18e7ed5a56c9 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -270,11 +270,8 @@ impl Config { // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. // Cache the entries when we extract it so we only have to read it once. - let mut recorded_entries = if dst.ends_with("ci-rustc") && pattern == "rustc-dev" { - Some(BufWriter::new(t!(File::create(dst.join(".rustc-dev-contents"))))) - } else { - None - }; + let mut recorded_entries = + if dst.ends_with("ci-rustc") { recorded_entries(dst, pattern) } else { None }; for member in t!(tar.entries()) { let mut member = t!(member); @@ -331,6 +328,17 @@ impl Config { } } +fn recorded_entries(dst: &Path, pattern: &str) -> Option> { + let name = if pattern == "rustc-dev" { + ".rustc-dev-contents" + } else if pattern.starts_with("rust-std") { + ".rust-std-contents" + } else { + return None; + }; + Some(BufWriter::new(t!(File::create(dst.join(name))))) +} + enum DownloadSource { CI, Dist, @@ -381,11 +389,20 @@ impl Config { Some(rustfmt_path) } - pub(crate) fn rustc_dev_contents(&self) -> Vec { + pub(crate) fn ci_rust_std_contents(&self) -> Vec { + self.ci_component_contents(".rust-std-contents") + } + + pub(crate) fn ci_rustc_dev_contents(&self) -> Vec { + self.ci_component_contents(".rustc-dev-contents") + } + + fn ci_component_contents(&self, stamp_file: &str) -> Vec { assert!(self.download_rustc()); let ci_rustc_dir = self.out.join(&*self.build.triple).join("ci-rustc"); - let rustc_dev_contents_file = t!(File::open(ci_rustc_dir.join(".rustc-dev-contents"))); - t!(BufReader::new(rustc_dev_contents_file).lines().collect()) + let stamp_file = ci_rustc_dir.join(stamp_file); + let contents_file = t!(File::open(&stamp_file), stamp_file.display().to_string()); + t!(BufReader::new(contents_file).lines().collect()) } pub(crate) fn download_ci_rustc(&self, commit: &str) { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 9f365474a70c8..ed524a2c4b1b4 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1777,7 +1777,13 @@ impl Step for BookTest { /// This uses the `rustdoc` that sits next to `compiler`. fn run(self, builder: &Builder<'_>) { let host = self.compiler.host; - let _guard = builder.msg(Kind::Test, self.compiler.stage, &format!("book {}", self.name), host, host); + let _guard = builder.msg( + Kind::Test, + self.compiler.stage, + &format!("book {}", self.name), + host, + host, + ); // External docs are different from local because: // - Some books need pre-processing by mdbook before being tested. // - They need to save their state to toolstate. @@ -2202,7 +2208,8 @@ impl Step for Crate { let target = self.target; let mode = self.mode; - builder.ensure(compile::Std::new(compiler, target)); + // See [field@compile::Std::force_recompile]. + builder.ensure(compile::Std::force_recompile(compiler, target)); builder.ensure(RemoteCopyLibs { compiler, target }); // If we're not doing a full bootstrap but we're testing a stage2 @@ -2216,6 +2223,16 @@ impl Step for Crate { match mode { Mode::Std => { compile::std_cargo(builder, target, compiler.stage, &mut cargo); + // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, + // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. + // Override it. + if builder.download_rustc() { + let sysroot = builder + .out + .join(compiler.host.triple) + .join(format!("stage{}-test-sysroot", compiler.stage)); + cargo.env("RUSTC_SYSROOT", sysroot); + } } Mode::Rustc => { compile::rustc_cargo(builder, &mut cargo, target, compiler.stage); @@ -2267,6 +2284,11 @@ impl Step for CrateRustdoc { // isn't really necessary. builder.compiler_for(builder.top_stage, target, target) }; + // NOTE: normally `ensure(Rustc)` automatically runs `ensure(Std)` for us. However, when + // using `download-rustc`, the rustc_private artifacts may be in a *different sysroot* from + // the target rustdoc (`ci-rustc-sysroot` vs `stage2`). In that case, we need to ensure this + // explicitly to make sure it ends up in the stage2 sysroot. + builder.ensure(compile::Std::new(compiler, target)); builder.ensure(compile::Rustc::new(compiler, target)); let mut cargo = tool::prepare_tool_cargo( @@ -2318,7 +2340,13 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - let _guard = builder.msg(builder.kind, compiler.stage, "rustdoc", compiler.host, target); + let _guard = builder.msg_sysroot_tool( + builder.kind, + compiler.stage, + "rustdoc", + compiler.host, + target, + ); run_cargo_test( cargo, &[], From 1570299af4df53aacd6d68cf1b24aee734e9cdcd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 2 Nov 2022 11:57:40 +0000 Subject: [PATCH 559/806] Remove const eval limit and implement an exponential backoff lint instead --- crates/hir-def/src/builtin_attr.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/hir-def/src/builtin_attr.rs b/crates/hir-def/src/builtin_attr.rs index f7c1e683d0d20..142b12290194e 100644 --- a/crates/hir-def/src/builtin_attr.rs +++ b/crates/hir-def/src/builtin_attr.rs @@ -195,10 +195,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), - gated!( - const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, - const_eval_limit, experimental!(const_eval_limit) - ), gated!( move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, large_assignments, experimental!(move_size_limit) From 1a5db18b1172de35b7f491d28dd2ed1542c4b0ad Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 14 May 2023 18:35:13 +0200 Subject: [PATCH 560/806] Drop uplifted clippy::cast_ref_to_mut --- clippy_lints/src/casts/cast_ref_to_mut.rs | 26 ------ clippy_lints/src/casts/mod.rs | 38 -------- clippy_lints/src/declared_lints.rs | 1 - clippy_lints/src/renamed_lints.rs | 1 + tests/ui/cast_ref_to_mut.rs | 31 ------- tests/ui/cast_ref_to_mut.stderr | 22 ----- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 106 ++++++++++++---------- 9 files changed, 61 insertions(+), 168 deletions(-) delete mode 100644 clippy_lints/src/casts/cast_ref_to_mut.rs delete mode 100644 tests/ui/cast_ref_to_mut.rs delete mode 100644 tests/ui/cast_ref_to_mut.stderr diff --git a/clippy_lints/src/casts/cast_ref_to_mut.rs b/clippy_lints/src/casts/cast_ref_to_mut.rs deleted file mode 100644 index 15f2f81f4079e..0000000000000 --- a/clippy_lints/src/casts/cast_ref_to_mut.rs +++ /dev/null @@ -1,26 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::ty; - -use super::CAST_REF_TO_MUT; - -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; - if let ExprKind::Cast(e, t) = &e.kind; - if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; - if let ExprKind::Cast(e, t) = &e.kind; - if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); - then { - span_lint( - cx, - CAST_REF_TO_MUT, - expr.span, - "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`", - ); - } - } -} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cfeb75eed3bb9..0c175372aab83 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -9,7 +9,6 @@ mod cast_possible_truncation; mod cast_possible_wrap; mod cast_precision_loss; mod cast_ptr_alignment; -mod cast_ref_to_mut; mod cast_sign_loss; mod cast_slice_different_sizes; mod cast_slice_from_raw_parts; @@ -330,41 +329,6 @@ declare_clippy_lint! { "casting a function pointer to any integer type" } -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of `&T` to `&mut T` anywhere in the code. - /// - /// ### Why is this bad? - /// It’s basically guaranteed to be undefined behavior. - /// `UnsafeCell` is the only way to obtain aliasable data that is considered - /// mutable. - /// - /// ### Example - /// ```rust,ignore - /// fn x(r: &i32) { - /// unsafe { - /// *(r as *const _ as *mut _) += 1; - /// } - /// } - /// ``` - /// - /// Instead consider using interior mutability types. - /// - /// ```rust - /// use std::cell::UnsafeCell; - /// - /// fn x(r: &UnsafeCell) { - /// unsafe { - /// *r.get() += 1; - /// } - /// } - /// ``` - #[clippy::version = "1.33.0"] - pub CAST_REF_TO_MUT, - correctness, - "a cast of reference to a mutable pointer" -} - declare_clippy_lint! { /// ### What it does /// Checks for expressions where a character literal is cast @@ -680,7 +644,6 @@ impl_lint_pass!(Casts => [ CAST_POSSIBLE_TRUNCATION, CAST_POSSIBLE_WRAP, CAST_LOSSLESS, - CAST_REF_TO_MUT, CAST_PTR_ALIGNMENT, CAST_SLICE_DIFFERENT_SIZES, UNNECESSARY_CAST, @@ -747,7 +710,6 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } - cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, &self.msrv); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0ae95b045e03c..212e809d4c564 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -81,7 +81,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::CAST_POSSIBLE_WRAP_INFO, crate::casts::CAST_PRECISION_LOSS_INFO, crate::casts::CAST_PTR_ALIGNMENT_INFO, - crate::casts::CAST_REF_TO_MUT_INFO, crate::casts::CAST_SIGN_LOSS_INFO, crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 7c2a100efdac6..4cb4183002339 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::stutter", "clippy::module_name_repetitions"), ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), + ("clippy::cast_ref_to_mut", "cast_ref_to_mut"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs deleted file mode 100644 index c48a734ba32c2..0000000000000 --- a/tests/ui/cast_ref_to_mut.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(clippy::cast_ref_to_mut)] -#![allow(clippy::no_effect, clippy::borrow_as_ptr)] - -extern "C" { - // N.B., mutability can be easily incorrect in FFI calls -- as - // in C, the default is mutable pointers. - fn ffi(c: *mut u8); - fn int_ffi(c: *mut i32); -} - -fn main() { - let s = String::from("Hello"); - let a = &s; - unsafe { - let num = &3i32; - let mut_num = &mut 3i32; - // Should be warned against - (*(a as *const _ as *mut String)).push_str(" world"); - *(a as *const _ as *mut _) = String::from("Replaced"); - *(a as *const _ as *mut String) += " world"; - // Shouldn't be warned against - println!("{}", *(num as *const _ as *const i16)); - println!("{}", *(mut_num as *mut _ as *mut i16)); - ffi(a.as_ptr() as *mut _); - int_ffi(num as *const _ as *mut _); - int_ffi(&3 as *const _ as *mut _); - let mut value = 3; - let value: *const i32 = &mut value; - *(value as *const i16 as *mut i16) = 42; - } -} diff --git a/tests/ui/cast_ref_to_mut.stderr b/tests/ui/cast_ref_to_mut.stderr deleted file mode 100644 index aacd99437d9fc..0000000000000 --- a/tests/ui/cast_ref_to_mut.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:18:9 - | -LL | (*(a as *const _ as *mut String)).push_str(" world"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` - -error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:19:9 - | -LL | *(a as *const _ as *mut _) = String::from("Replaced"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:20:9 - | -LL | *(a as *const _ as *mut String) += " world"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 53ac65473b827..f7854b89ee4ea 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -29,6 +29,7 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] +#![allow(cast_ref_to_mut)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] @@ -76,6 +77,7 @@ #![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] #![warn(clippy::invisible_characters)] +#![warn(cast_ref_to_mut)] #![warn(suspicious_double_ref_op)] #![warn(drop_bounds)] #![warn(dropping_copy_types)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 722c0b3eb2750..fa347d395ef5d 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -29,6 +29,7 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] +#![allow(cast_ref_to_mut)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] @@ -76,6 +77,7 @@ #![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] +#![warn(clippy::cast_ref_to_mut)] #![warn(clippy::clone_double_ref)] #![warn(clippy::drop_bounds)] #![warn(clippy::drop_copy)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 1ff8391766023..9dffe51e5d7d3 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,292 +7,298 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut` + --> $DIR/rename.rs:80:9 + | +LL | #![warn(clippy::cast_ref_to_mut)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut` + error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 49 previous errors +error: aborting due to 50 previous errors From ecb8616870aa9ef0c7159e56f563dc0ad78b32f7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 31 May 2023 15:37:35 +0200 Subject: [PATCH 561/806] fix: Don't duplicate sysroot crates in rustc workspace --- crates/hir/src/lib.rs | 4 +-- .../src/handlers/mutability_errors.rs | 3 +- .../ide/src/inlay_hints/closure_captures.rs | 2 +- crates/project-model/src/workspace.rs | 33 ++++++++++++++----- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index eee45b74d94dc..7c432197a60a7 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2703,8 +2703,8 @@ impl LocalSource { self.source.file_id } - pub fn name(&self) -> Option { - self.source.value.name() + pub fn name(&self) -> Option> { + self.source.as_ref().map(|it| it.name()).transpose() } pub fn syntax(&self) -> &SyntaxNode { diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 45b44c2c5ca91..28dadae8d3ef9 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -18,7 +18,8 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno 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()); + // FIXME: macros + edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_string()); } let edit = edit_builder.finish(); Some(vec![fix( diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index 3ee118f6e8c79..9d5defcbb71a9 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -74,7 +74,7 @@ pub(super) fn hints( capture.display_place(sema.db) ), None, - source.name().and_then(|name| sema.original_range_opt(name.syntax())), + source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), ), text_edit: None, position: InlayHintPosition::After, diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index a695bc1cca78c..0aca620a67553 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -24,7 +24,7 @@ use crate::{ rustc_cfg, sysroot::SysrootCrate, target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, - Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts, + Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; /// A set of cfg-overrides per crate. @@ -900,7 +900,24 @@ fn cargo_to_crate_graph( // https://github.com/rust-lang/rust-analyzer/issues/11300 continue; } - let Some(file_id) = load(&cargo[tgt].root) else { continue }; + let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt]; + + if kind == TargetKind::Lib + && sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root())) + { + if let Some(&(_, crate_id, _)) = + public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name) + { + pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind)); + + lib_tgt = Some((crate_id, name.clone())); + pkg_to_lib_crate.insert(pkg, crate_id); + // sysroot is inside the workspace, prevent the sysroot crates from being duplicated here + continue; + } + } + + let Some(file_id) = load(root) else { continue }; let crate_id = add_target_crate_root( crate_graph, @@ -909,23 +926,23 @@ fn cargo_to_crate_graph( build_scripts.get_output(pkg), cfg_options.clone(), file_id, - &cargo[tgt].name, - cargo[tgt].is_proc_macro, + name, + is_proc_macro, target_layout.clone(), false, channel, ); - if cargo[tgt].kind == TargetKind::Lib { - lib_tgt = Some((crate_id, cargo[tgt].name.clone())); + if kind == TargetKind::Lib { + lib_tgt = Some((crate_id, name.clone())); pkg_to_lib_crate.insert(pkg, crate_id); } // Even crates that don't set proc-macro = true are allowed to depend on proc_macro // (just none of the APIs work when called outside of a proc macro). if let Some(proc_macro) = libproc_macro { - add_proc_macro_dep(crate_graph, crate_id, proc_macro, cargo[tgt].is_proc_macro); + add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro); } - pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind)); + pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind)); } // Set deps to the core, std and to the lib target of the current package From de62139513b486ae4cdf1ca68c6eae5daca25cc8 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 31 May 2023 14:30:00 +0000 Subject: [PATCH 562/806] Remove lint name and category fields from the new lint issue form --- .github/ISSUE_TEMPLATE/new_lint.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index 0b43d8d70c007..b49493edce1ba 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -12,29 +12,6 @@ body: description: What does this lint do? validations: required: true - - type: input - id: lint-name - attributes: - label: Lint Name - description: Please provide the lint name. - - type: dropdown - id: category - attributes: - label: Category - description: > - What category should this lint go into? If you're unsure you can select - multiple categories. You can find a category description in the - `README`. - multiple: true - options: - - correctness - - suspicious - - style - - complexity - - perf - - pedantic - - restriction - - cargo - type: textarea id: advantage attributes: From a17561ffc90c900cb7d0e96b00c6381244764ef7 Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 16 May 2023 18:43:23 +0300 Subject: [PATCH 563/806] use new c literals instead of cstr! macro --- Cargo.lock | 11 ----- compiler/rustc_codegen_llvm/Cargo.toml | 1 - compiler/rustc_codegen_llvm/src/allocator.rs | 4 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 8 ++-- compiler/rustc_codegen_llvm/src/base.rs | 6 +-- compiler/rustc_codegen_llvm/src/builder.rs | 16 ++----- compiler/rustc_codegen_llvm/src/consts.rs | 7 ++- compiler/rustc_codegen_llvm/src/context.rs | 44 ++++++++++--------- .../rustc_codegen_llvm/src/debuginfo/gdb.rs | 3 +- .../src/debuginfo/metadata.rs | 9 ++-- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 7 ++- compiler/rustc_codegen_llvm/src/lib.rs | 1 + 13 files changed, 48 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7aa317ad7501..2375d51098dc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -872,16 +872,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cstr" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11a39d776a3b35896711da8a04dc1835169dcd36f710878187637314e47941b" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "ctrlc" version = "3.3.1" @@ -3180,7 +3170,6 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", - "cstr", "libc", "measureme", "object 0.31.1", diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index ad51f2d095857..39ff3a0ba2d22 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -8,7 +8,6 @@ test = false [dependencies] bitflags = "1.0" -cstr = "0.2" libc = "0.2" measureme = "10.0.0" object = { version = "0.31.1", default-features = false, features = [ diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index a57508815d6f8..ad0636894b793 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -77,7 +77,7 @@ pub(crate) unsafe fn codegen( llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); @@ -129,7 +129,7 @@ pub(crate) unsafe fn codegen( attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 604f68eb6a47e..8b05af7bed909 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -595,7 +595,7 @@ pub(crate) fn run_pass_manager( llvm::LLVMRustAddModuleFlag( module.module_llvm.llmod(), llvm::LLVMModFlagBehavior::Error, - "LTOPostLink\0".as_ptr().cast(), + c"LTOPostLink".as_ptr().cast(), 1, ); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ca2eab28f872b..53b4296802ef7 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -891,11 +891,11 @@ unsafe fn embed_bitcode( let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.module\0".as_ptr().cast(), + c"rustc.embedded.module".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { "__LLVM,__bitcode\0" } else { ".llvmbc\0" }; + let section = if is_apple { c"__LLVM,__bitcode" } else { c".llvmbc" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); @@ -904,10 +904,10 @@ unsafe fn embed_bitcode( let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.cmdline\0".as_ptr().cast(), + c"rustc.embedded.cmdline".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" }; + let section = if is_apple { c"__LLVM,__cmdline" } else { c".llvmcmd" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); } else { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 5b2bbdb4bde1e..2f7eb08ad3d4c 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -19,8 +19,6 @@ use crate::context::CodegenCx; use crate::llvm; use crate::value::Value; -use cstr::cstr; - use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; @@ -110,11 +108,11 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen // Create the llvm.used and llvm.compiler.used variables. if !cx.used_statics.borrow().is_empty() { - cx.create_used_variable_impl(cstr!("llvm.used"), &*cx.used_statics.borrow()); + cx.create_used_variable_impl(c"llvm.used", &*cx.used_statics.borrow()); } if !cx.compiler_used_statics.borrow().is_empty() { cx.create_used_variable_impl( - cstr!("llvm.compiler.used"), + c"llvm.compiler.used", &*cx.compiler_used_statics.borrow(), ); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4d0bcd53d1562..992b45bd6746c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -6,7 +6,6 @@ use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True} use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use cstr::cstr; use libc::{c_char, c_uint}; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -25,7 +24,6 @@ use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, Typ use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use std::borrow::Cow; -use std::ffi::CStr; use std::iter; use std::ops::Deref; use std::ptr; @@ -45,13 +43,10 @@ impl Drop for Builder<'_, '_, '_> { } } -// FIXME(eddyb) use a checked constructor when they become `const fn`. -const EMPTY_C_STR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; - /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. -const UNNAMED: *const c_char = EMPTY_C_STR.as_ptr(); +const UNNAMED: *const c_char = c"".as_ptr(); impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> { type Value = as BackendTypes>::Value; @@ -1010,14 +1005,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn cleanup_pad(&mut self, parent: Option<&'ll Value>, args: &[&'ll Value]) -> Funclet<'ll> { - let name = cstr!("cleanuppad"); let ret = unsafe { llvm::LLVMBuildCleanupPad( self.llbuilder, parent, args.as_ptr(), args.len() as c_uint, - name.as_ptr(), + c"cleanuppad".as_ptr(), ) }; Funclet::new(ret.expect("LLVM does not have support for cleanuppad")) @@ -1031,14 +1025,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn catch_pad(&mut self, parent: &'ll Value, args: &[&'ll Value]) -> Funclet<'ll> { - let name = cstr!("catchpad"); let ret = unsafe { llvm::LLVMBuildCatchPad( self.llbuilder, parent, args.as_ptr(), args.len() as c_uint, - name.as_ptr(), + c"catchpad".as_ptr(), ) }; Funclet::new(ret.expect("LLVM does not have support for catchpad")) @@ -1050,14 +1043,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unwind: Option<&'ll BasicBlock>, handlers: &[&'ll BasicBlock], ) -> &'ll Value { - let name = cstr!("catchswitch"); let ret = unsafe { llvm::LLVMBuildCatchSwitch( self.llbuilder, parent, unwind, handlers.len() as c_uint, - name.as_ptr(), + c"catchswitch".as_ptr(), ) }; let ret = ret.expect("LLVM does not have support for catchswitch"); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 940358acde9a2..8bcd735ab325c 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -6,7 +6,6 @@ use crate::llvm::{self, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -473,9 +472,9 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { .all(|&byte| byte == 0); let sect_name = if all_bytes_are_zero { - cstr!("__DATA,__thread_bss") + c"__DATA,__thread_bss" } else { - cstr!("__DATA,__thread_data") + c"__DATA,__thread_data" }; llvm::LLVMSetSection(g, sect_name.as_ptr()); } @@ -504,7 +503,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { let val = llvm::LLVMMetadataAsValue(self.llcx, meta); llvm::LLVMAddNamedMetadataOperand( self.llmod, - "wasm.custom_sections\0".as_ptr().cast(), + c"wasm.custom_sections".as_ptr().cast(), val, ); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 83101a85435a0..ff996b93a4c3e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -8,7 +8,6 @@ use crate::llvm_util; use crate::type_::Type; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n; @@ -224,36 +223,42 @@ pub unsafe fn create_module<'ll>( // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { - let avoid_plt = "RtLibUseGOT\0".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + c"RtLibUseGOT".as_ptr().cast(), + 1, + ); } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { - let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast(); llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - canonical_jump_tables, + c"CFI Canonical Jump Tables".as_ptr().cast(), 1, ); } // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { - let enable_split_lto_unit = "EnableSplitLTOUnit\0".as_ptr().cast(); llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - enable_split_lto_unit, + c"EnableSplitLTOUnit".as_ptr().cast(), 1, ); } // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { - let kcfi = "kcfi\0".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Override, + c"kcfi".as_ptr().cast(), + 1, + ); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -265,7 +270,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, - "cfguard\0".as_ptr() as *const _, + c"cfguard".as_ptr() as *const _, 1, ) } @@ -274,7 +279,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, - "cfguard\0".as_ptr() as *const _, + c"cfguard".as_ptr() as *const _, 2, ) } @@ -292,26 +297,26 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, behavior, - "branch-target-enforcement\0".as_ptr().cast(), + c"branch-target-enforcement".as_ptr().cast(), bti.into(), ); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address\0".as_ptr().cast(), + c"sign-return-address".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address-all\0".as_ptr().cast(), + c"sign-return-address-all".as_ptr().cast(), pac_opts.leaf.into(), ); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address-with-bkey\0".as_ptr().cast(), + c"sign-return-address-with-bkey".as_ptr().cast(), u32::from(pac_opts.key == PAuthKey::B), ); } else { @@ -327,7 +332,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - "cf-protection-branch\0".as_ptr().cast(), + c"cf-protection-branch".as_ptr().cast(), 1, ) } @@ -335,7 +340,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - "cf-protection-return\0".as_ptr().cast(), + c"cf-protection-return".as_ptr().cast(), 1, ) } @@ -344,7 +349,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Error, - "Virtual Function Elim\0".as_ptr().cast(), + c"Virtual Function Elim".as_ptr().cast(), 1, ); } @@ -476,14 +481,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { - let section = cstr!("llvm.metadata"); let array = self.const_array(self.type_ptr_to(self.type_i8()), values); unsafe { let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); llvm::LLVMSetInitializer(g, array); llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); + llvm::LLVMSetSection(g, c"llvm.metadata".as_ptr()); } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 37f30917609ae..8be54b7eb7188 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -38,7 +38,6 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, c_section_var_name.as_ptr().cast()) }; section_var.unwrap_or_else(|| { - let section_name = b".debug_gdb_scripts\0"; let mut section_contents = Vec::new(); // Add the pretty printers for the standard library first. @@ -71,7 +70,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' let section_var = cx .define_global(section_var_name, llvm_type) .unwrap_or_else(|| bug!("symbol `{}` is already defined", section_var_name)); - llvm::LLVMSetSection(section_var, section_name.as_ptr().cast()); + llvm::LLVMSetSection(section_var, c".debug_gdb_scripts".as_ptr().cast()); llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index bd2fba1260282..166454d3ae74c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -20,7 +20,6 @@ use crate::llvm::debuginfo::{ }; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind; use rustc_codegen_ssa::traits::*; @@ -812,7 +811,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped); - let flags = "\0"; let output_filenames = tcx.output_filenames(()); let split_name = if tcx.sess.target_can_use_split_dwarf() { output_filenames @@ -849,7 +847,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( producer.as_ptr().cast(), producer.len(), tcx.sess.opts.optimize != config::OptLevel::No, - flags.as_ptr().cast(), + c"".as_ptr().cast(), 0, // NB: this doesn't actually have any perceptible effect, it seems. LLVM will instead // put the path supplied to `MCSplitDwarfFile` into the debug info of the final @@ -878,8 +876,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( ); let val = llvm::LLVMMetadataAsValue(debug_context.llcontext, gcov_metadata); - let llvm_gcov_ident = cstr!("llvm.gcov"); - llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val); + llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, c"llvm.gcov".as_ptr(), val); } // Insert `llvm.ident` metadata on the wasm targets since that will @@ -892,7 +889,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( ); llvm::LLVMAddNamedMetadataOperand( debug_context.llmod, - cstr!("llvm.ident").as_ptr(), + c"llvm.ident".as_ptr(), llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), ); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c3f0a0033b0ea..aa7ae9355bcfa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -113,7 +113,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - "Dwarf Version\0".as_ptr().cast(), + c"Dwarf Version".as_ptr().cast(), dwarf_version, ); } else { @@ -121,17 +121,16 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - "CodeView\0".as_ptr().cast(), + c"CodeView".as_ptr().cast(), 1, ) } // Prevent bitcode readers from deleting the debug info. - let ptr = "Debug Info Version\0".as_ptr(); llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - ptr.cast(), + c"Debug Info Version".as_ptr().cast(), llvm::LLVMRustDebugMetadataVersion(), ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 805843e5863e6..ff9909c720e54 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -11,6 +11,7 @@ #![feature(let_chains)] #![feature(never_type)] #![feature(impl_trait_in_assoc_type)] +#![feature(c_str_literals)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] From f212ba6d6d60963c8101bb24fc3e53fca80c046f Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 16 May 2023 19:37:26 +0300 Subject: [PATCH 564/806] use c literals in library --- library/std/src/lib.rs | 1 + library/std/src/sys/unix/args.rs | 12 ++++++------ library/std/src/sys/unix/fs.rs | 2 +- library/std/src/sys/unix/mod.rs | 7 +++---- library/std/src/sys/unix/process/process_common.rs | 9 ++++----- library/std/src/sys/unix/thread.rs | 3 +-- library/std/src/sys/windows/c.rs | 4 ++-- library/std/src/sys/windows/compat.rs | 6 +++--- library/std/src/sys/windows/mod.rs | 4 ++-- src/tools/tidy/src/deps.rs | 1 - 10 files changed, 23 insertions(+), 26 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 318a46d1b637e..d53f1a2b2fff1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -240,6 +240,7 @@ #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(c_str_literals)] #![feature(c_unwind)] #![feature(cfg_target_thread_local)] #![feature(concat_idents)] diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 9ed4d9c1e0dd3..1e4c2445232db 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -242,13 +242,13 @@ mod imp { let mut res = Vec::new(); unsafe { - let process_info_sel = sel_registerName("processInfo\0".as_ptr()); - let arguments_sel = sel_registerName("arguments\0".as_ptr()); - let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); - let count_sel = sel_registerName("count\0".as_ptr()); - let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); + let process_info_sel = sel_registerName(c"processInfo".as_ptr()); + let arguments_sel = sel_registerName(c"arguments".as_ptr()); + let utf8_sel = sel_registerName(c"UTF8String".as_ptr()); + let count_sel = sel_registerName(c"count".as_ptr()); + let object_at_sel = sel_registerName(c"objectAtIndex:".as_ptr()); - let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let klass = objc_getClass(c"NSProcessInfo".as_ptr()); let info = objc_msgSend(klass, process_info_sel); let args = objc_msgSend(info, arguments_sel); diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 09e9ae2720f5b..0e5691d40d176 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1063,7 +1063,7 @@ impl File { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( fd, - b"\0" as *const _ as *const c_char, + c"".as_ptr() as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, libc::STATX_ALL, ) } { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index bb9e65e68e5e8..54e2f20b31703 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -1,6 +1,5 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::CStr; use crate::io::ErrorKind; pub use self::rand::hashmap_random_keys; @@ -75,7 +74,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_os = "macos") { - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } unsafe fn sanitize_standard_fds() { @@ -121,7 +120,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -151,7 +150,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { use libc::open64; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 640648e870748..5f316b12b625d 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -24,11 +24,11 @@ cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { // fuchsia doesn't have /dev/null } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &str = "null:\0"; + const DEV_NULL: &CStr = c"null:"; } else if #[cfg(target_os = "vxworks")] { - const DEV_NULL: &str = "/null\0"; + const DEV_NULL: &CStr = c"/null"; } else { - const DEV_NULL: &str = "/dev/null\0"; + const DEV_NULL: &CStr = c"/dev/null"; } } @@ -474,8 +474,7 @@ impl Stdio { let mut opts = OpenOptions::new(); opts.read(readable); opts.write(!readable); - let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; + let fd = File::open_c(DEV_NULL, &opts)?; Ok((ChildStdio::Owned(fd.into_inner()), None)) } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7307d9b2c8616..878af5088d9f4 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -163,10 +163,9 @@ impl Thread { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { unsafe { - let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice()); let res = libc::pthread_setname_np( libc::pthread_self(), - cname.as_ptr(), + c"%s".as_ptr(), name.as_ptr() as *mut libc::c_void, ); debug_assert_eq!(res, 0); diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 2bc40c4748a35..07b0610d463fa 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -317,7 +317,7 @@ pub unsafe fn NtWriteFile( // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { - pub static KERNEL32: &CStr = ansi_str!("kernel32"); + pub static KERNEL32: &CStr = c"kernel32"; // >= Win10 1607 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription @@ -350,7 +350,7 @@ compat_fn_optional! { } compat_fn_with_fallback! { - pub static NTDLL: &CStr = ansi_str!("ntdll"); + pub static NTDLL: &CStr = c"ntdll"; pub fn NtCreateKeyedEvent( KeyedEventHandle: LPHANDLE, diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 4fe95d41116b5..649cc4bfdbf1a 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -228,9 +228,9 @@ macro_rules! compat_fn_optional { /// Load all needed functions from "api-ms-win-core-synch-l1-2-0". pub(super) fn load_synch_functions() { fn try_load() -> Option<()> { - const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0"); - const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress"); - const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle"); + const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0"; + const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress"; + const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle"; // Try loading the library and all the required functions. // If any step fails, then they all fail. diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index bcc172b0fae36..b11c89622032e 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -1,6 +1,6 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; @@ -51,7 +51,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } // SAFETY: must be called only once during runtime cleanup. diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index db2b7910b711c..abaed95a08426 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -132,7 +132,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "crossbeam-epoch", "crossbeam-utils", "crypto-common", - "cstr", "datafrog", "derive_more", "digest", From 0a61bc4d36e737cb6dca3d3953985a5ad469387d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 1 Jun 2023 03:27:16 +0900 Subject: [PATCH 565/806] Support 128-bit atomics on all x86_64 Apple targets --- compiler/rustc_target/src/spec/x86_64_apple_ios.rs | 2 +- compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs | 2 +- compiler/rustc_target/src/spec/x86_64_apple_tvos.rs | 2 +- compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index 1dcb47056a463..061b6a96fc888 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -13,7 +13,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..base }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 9f3b0fab697e6..50f359c357bdf 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -15,7 +15,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..base }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index 550ce0b9ce577..76de7d20c4c6f 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..opts("tvos", arch) }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs index 75ce02cba1de0..5fcc00a86ff95 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, forces_embed_bitcode: true, // Taken from a clang build on Xcode 11.4.1. From 38c0ba7d0dbb2ad69650e959050987a628037344 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 31 May 2023 16:05:12 -0500 Subject: [PATCH 566/806] Fix the progress message for `x doc rustc` This makes it more clear that we're using stage 0 *to document* rustc, not that we're documenting stage0 rustc itself. It also fixes a bug in `msg_sysroot_tool` that would print `Docing`, and removes the `Debug` impl for `Kind` to make sure it doesn't happen again. Before: ``` Documenting stage0 compiler {rustc-main} (aarch64-apple-darwin) ``` After: ``` Documenting compiler {rustc-main} (stage0 -> stage1, aarch64-apple-darwin) ``` --- src/bootstrap/builder.rs | 7 +++++-- src/bootstrap/doc.rs | 2 +- src/bootstrap/lib.rs | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 43c859b00631e..3b9270f563c1a 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -571,7 +571,7 @@ impl<'a> ShouldRun<'a> { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub enum Kind { #[clap(alias = "b")] Build, @@ -642,7 +642,10 @@ impl Kind { Kind::Doc => "Documenting", Kind::Run => "Running", Kind::Suggest => "Suggesting", - _ => return format!("{self:?}"), + _ => { + let title_letter = self.as_str()[0..1].to_ascii_uppercase(); + return format!("{title_letter}{}ing", &self.as_str()[1..]); + } } .to_owned() } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 3de85c91516c4..2e715fce945e1 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -680,7 +680,7 @@ impl Step for Rustc { let compiler = builder.compiler(stage, builder.config.build); builder.ensure(compile::Std::new(compiler, builder.config.build)); - let _guard = builder.msg( + let _guard = builder.msg_sysroot_tool( Kind::Doc, stage, &format!("compiler{}", crate_description(&self.crates)), diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a1aaee68c625d..aa5d1bdd37f99 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1045,8 +1045,8 @@ impl Build { what: impl Display, target: TargetSelection, ) -> Option { - let action = action.into(); - let msg = format!("{action:?}ing {what} for {target}"); + let action = action.into().description(); + let msg = format!("{action} {what} for {target}"); self.group(&msg) } @@ -1058,8 +1058,8 @@ impl Build { host: TargetSelection, target: TargetSelection, ) -> Option { - let action = action.into(); - let msg = |fmt| format!("{action:?}ing {what} {fmt}"); + let action = action.into().description(); + let msg = |fmt| format!("{action} {what} {fmt}"); let msg = if host == target { msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1)) } else { From f74ec6b1b858c13449dc4cfd9231a3a0d82e50df Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 9 Apr 2023 04:56:03 +0200 Subject: [PATCH 567/806] new lint: `missing_field_in_debug` move some strings into consts, more tests s/missing_field_in_debug/missing_fields_in_debug dont trigger in macro expansions make dogfood tests happy minor cleanups replace HashSet with FxHashSet replace match_def_path with match_type if_chain -> let chains, fix markdown, allow newtype pattern fmt consider string literal in `.field()` calls as used don't intern defined symbol, remove mentions of 'debug_tuple' special-case PD, account for field access through `Deref` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/missing_fields_in_debug.rs | 311 +++++++++++++++++++ clippy_utils/src/paths.rs | 2 + tests/ui/missing_fields_in_debug.rs | 319 ++++++++++++++++++++ tests/ui/missing_fields_in_debug.stderr | 113 +++++++ 7 files changed, 749 insertions(+) create mode 100644 clippy_lints/src/missing_fields_in_debug.rs create mode 100644 tests/ui/missing_fields_in_debug.rs create mode 100644 tests/ui/missing_fields_in_debug.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf0cacad3624..8b609b47d8192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4963,6 +4963,7 @@ Released 2018-09-13 [`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 [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc +[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4ade25e1257e0..a7067d8b86aaf 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -430,6 +430,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ 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, + crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO, crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10404d51ba556..a0a89e4967b8f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -203,6 +203,7 @@ mod missing_assert_message; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; +mod missing_fields_in_debug; mod missing_inline; mod missing_trait_methods; mod mixed_read_write_in_expression; @@ -994,6 +995,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); store.register_early_pass(|| Box::new(needless_else::NeedlessElse)); + store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs new file mode 100644 index 0000000000000..389a8d04dc289 --- /dev/null +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -0,0 +1,311 @@ +use std::ops::ControlFlow; + +use clippy_utils::{ + diagnostics::span_lint_and_then, + paths, + ty::match_type, + visitors::{for_each_expr, Visitable}, +}; +use rustc_ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{ + def::{DefKind, Res}, + Expr, ImplItemKind, MatchSource, Node, +}; +use rustc_hir::{Block, PatKind}; +use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind}; +use rustc_hir::{ImplItem, Item, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeckResults; +use rustc_middle::ty::{EarlyBinder, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual [`core::fmt::Debug`](https://doc.rust-lang.org/core/fmt/trait.Debug.html) implementations that do not use all fields. + /// + /// ### Why is this bad? + /// A common mistake is to forget to update manual `Debug` implementations when adding a new field + /// to a struct or a new variant to an enum. + /// + /// At the same time, it also acts as a style lint to suggest using [`core::fmt::DebugStruct::finish_non_exhaustive`](https://doc.rust-lang.org/core/fmt/struct.DebugStruct.html#method.finish_non_exhaustive) + /// for the times when the user intentionally wants to leave out certain fields (e.g. to hide implementation details). + /// + /// ### Known problems + /// This lint works based on the `DebugStruct` helper types provided by the `Formatter`, + /// so this won't detect `Debug` impls that use the `write!` macro. + /// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries + /// to be on the conservative side and not lint in those cases in an attempt to prevent false positives. + /// + /// This lint also does not look through function calls, so calling `.field(self.as_slice())` for example + /// does not consider fields used inside of `as_slice()` as used by the `Debug` impl. + /// + /// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive` + /// method. + /// + /// ### Example + /// ```rust + /// use std::fmt; + /// struct Foo { + /// data: String, + /// // implementation detail + /// hidden_data: i32 + /// } + /// impl fmt::Debug for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + /// formatter + /// .debug_struct("Foo") + /// .field("data", &self.data) + /// .finish() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// struct Foo { + /// data: String, + /// // implementation detail + /// hidden_data: i32 + /// } + /// impl fmt::Debug for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + /// formatter + /// .debug_struct("Foo") + /// .field("data", &self.data) + /// .finish_non_exhaustive() + /// } + /// } + /// ``` + #[clippy::version = "1.70.0"] + pub MISSING_FIELDS_IN_DEBUG, + pedantic, + "missing fields in manual `Debug` implementation" +} +declare_lint_pass!(MissingFieldsInDebug => [MISSING_FIELDS_IN_DEBUG]); + +fn report_lints(cx: &LateContext<'_>, span: Span, span_notes: Vec<(Span, &'static str)>) { + span_lint_and_then( + cx, + MISSING_FIELDS_IN_DEBUG, + span, + "manual `Debug` impl does not include all fields", + |diag| { + for (span, note) in span_notes { + diag.span_note(span, note); + } + diag.help("consider including all fields in this `Debug` impl") + .help("consider calling `.finish_non_exhaustive()` if you intend to ignore fields"); + }, + ); +} + +/// Checks if we should lint in a block of code +/// +/// The way we check for this condition is by checking if there is +/// a call to `Formatter::debug_struct` but no call to `.finish_non_exhaustive()`. +fn should_lint<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + block: impl Visitable<'tcx>, +) -> bool { + // Is there a call to `DebugStruct::finish_non_exhaustive`? Don't lint if there is. + let mut has_finish_non_exhaustive = false; + // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. + let mut has_debug_struct = false; + + for_each_expr(block, |expr| { + if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { + let recv_ty = typeck_results.expr_ty(recv).peel_refs(); + + if path.ident.name == sym::debug_struct && match_type(cx, recv_ty, &paths::FORMATTER) { + has_debug_struct = true; + } else if path.ident.name == sym!(finish_non_exhaustive) && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) { + has_finish_non_exhaustive = true; + } + } + ControlFlow::::Continue(()) + }); + + !has_finish_non_exhaustive && has_debug_struct +} + +/// Checks if the given expression is a call to `DebugStruct::field` +/// and the first argument to it is a string literal and if so, returns it +/// +/// Example: `.field("foo", ....)` returns `Some("foo")` +fn as_field_call<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + expr: &Expr<'_>, +) -> Option { + if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind + && let recv_ty = typeck_results.expr_ty(recv).peel_refs() + && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) + && path.ident.name == sym::field + && let ExprKind::Lit(lit) = &debug_field.kind + && let LitKind::Str(sym, ..) = lit.node + { + Some(sym) + } else { + None + } +} + +/// Attempts to find unused fields assuming that the item is a struct +fn check_struct<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + block: &'tcx Block<'tcx>, + self_ty: Ty<'tcx>, + item: &'tcx Item<'tcx>, + data: &VariantData<'_>, +) { + // Is there a "direct" field access anywhere (i.e. self.foo)? + // We don't want to lint if there is not, because the user might have + // a newtype struct and use fields from the wrapped type only. + let mut has_direct_field_access = false; + let mut field_accesses = FxHashSet::default(); + + for_each_expr(block, |expr| { + if let ExprKind::Field(target, ident) = expr.kind + && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() + && target_ty == self_ty + { + field_accesses.insert(ident.name); + has_direct_field_access = true; + } else if let Some(sym) = as_field_call(cx, typeck_results, expr) { + field_accesses.insert(sym); + } + ControlFlow::::Continue(()) + }); + + let span_notes = data + .fields() + .iter() + .filter_map(|field| { + let EarlyBinder(field_ty) = cx.tcx.type_of(field.def_id); + if field_accesses.contains(&field.ident.name) || field_ty.is_phantom_data() { + None + } else { + Some((field.span, "this field is unused")) + } + }) + .collect::>(); + + // only lint if there's also at least one direct field access to allow patterns + // where one might have a newtype struct and uses fields from the wrapped type + if !span_notes.is_empty() && has_direct_field_access { + report_lints(cx, item.span, span_notes); + } +} + +/// Attempts to find unused fields in variants assuming that +/// the item is an enum. +/// +/// Currently, only simple cases are detected where the user +/// matches on `self` and calls `debug_struct` inside of the arms +fn check_enum<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + block: &'tcx Block<'tcx>, + self_ty: Ty<'tcx>, + item: &'tcx Item<'tcx>, +) { + let Some(arms) = for_each_expr(block, |expr| { + if let ExprKind::Match(val, arms, MatchSource::Normal) = expr.kind + && let match_ty = typeck_results.expr_ty_adjusted(val).peel_refs() + && match_ty == self_ty + { + ControlFlow::Break(arms) + } else { + ControlFlow::Continue(()) + } + }) else { + return; + }; + + let mut span_notes = Vec::new(); + + for arm in arms { + if !should_lint(cx, typeck_results, arm.body) { + continue; + } + + arm.pat.walk_always(|pat| match pat.kind { + PatKind::Wild => span_notes.push((pat.span, "unused field here due to wildcard `_`")), + PatKind::Tuple(_, rest) | PatKind::TupleStruct(.., rest) if rest.as_opt_usize().is_some() => { + span_notes.push((pat.span, "more unused fields here due to rest pattern `..`")); + }, + PatKind::Struct(.., true) => { + span_notes.push((pat.span, "more unused fields here due to rest pattern `..`")); + }, + _ => {}, + }); + + let mut field_accesses = FxHashSet::default(); + let mut check_field_access = |sym, expr| { + if !typeck_results.expr_ty(expr).is_phantom_data() { + arm.pat.each_binding(|_, _, _, pat_ident| { + if sym == pat_ident.name { + field_accesses.insert(pat_ident); + } + }); + } + }; + + for_each_expr(arm.body, |expr| { + if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind && let Some(segment) = path.segments.first() + { + check_field_access(segment.ident.name, expr); + } else if let Some(sym) = as_field_call(cx, typeck_results, expr) { + check_field_access(sym, expr); + } + ControlFlow::::Continue(()) + }); + + arm.pat.each_binding(|_, _, span, pat_ident| { + if !field_accesses.contains(&pat_ident) { + span_notes.push((span, "the field referenced by this binding is unused")); + } + }); + } + + if !span_notes.is_empty() { + report_lints(cx, item.span, span_notes); + } +} + +impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { + // is this an `impl Debug for X` block? + if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind + && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res + && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind + && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) + // don't trigger if this impl was derived + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !item.span.from_expansion() + // find `Debug::fmt` function + && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt) + && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir().impl_item(fmt_item.id) + && let body = cx.tcx.hir().body(*body_id) + && let ExprKind::Block(block, _) = body.value.kind + // inspect `self` + && let self_ty = cx.tcx.type_of(self_path.res.def_id()).0.peel_refs() + && let Some(self_adt) = self_ty.ty_adt_def() + && let Some(self_def_id) = self_adt.did().as_local() + && let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id) + // NB: can't call cx.typeck_results() as we are not in a body + && let typeck_results = cx.tcx.typeck_body(*body_id) + && should_lint(cx, typeck_results, block) + { + match &self_item.kind { + ItemKind::Struct(data, _) => check_struct(cx, typeck_results, block, self_ty, item, data), + ItemKind::Enum(..) => check_enum(cx, typeck_results, block, self_ty, item), + _ => {} + } + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 0f0792fdaa963..3a2b0a72a3de0 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -163,3 +163,5 @@ pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"]; pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; +pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"]; +pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; diff --git a/tests/ui/missing_fields_in_debug.rs b/tests/ui/missing_fields_in_debug.rs new file mode 100644 index 0000000000000..72b9e0e2aae5a --- /dev/null +++ b/tests/ui/missing_fields_in_debug.rs @@ -0,0 +1,319 @@ +#![allow(unused)] +#![warn(clippy::missing_fields_in_debug)] + +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; + +struct NamedStruct1Ignored { + data: u8, + hidden: u32, +} + +impl fmt::Debug for NamedStruct1Ignored { + // unused field: hidden + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStruct1Ignored") + .field("data", &self.data) + .finish() + } +} + +struct NamedStructMultipleIgnored { + data: u8, + hidden: u32, + hidden2: String, + hidden3: Vec>, + hidden4: ((((u8), u16), u32), u64), +} + +impl fmt::Debug for NamedStructMultipleIgnored { + // unused fields: hidden, hidden2, hidden4 + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStructMultipleIgnored") + .field("data", &self.data) + .field("hidden3", &self.hidden3) + .finish() + } +} + +struct Unit; + +// ok +impl fmt::Debug for Unit { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.debug_struct("Unit").finish() + } +} + +struct UnnamedStruct1Ignored(String); + +impl fmt::Debug for UnnamedStruct1Ignored { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.debug_tuple("UnnamedStruct1Ignored").finish() + } +} + +struct UnnamedStructMultipleIgnored(String, Vec, i32); + +// tuple structs are not linted +impl fmt::Debug for UnnamedStructMultipleIgnored { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_tuple("UnnamedStructMultipleIgnored") + .field(&self.1) + .finish() + } +} + +struct NamedStructNonExhaustive { + a: u8, + b: String, +} + +// ok +impl fmt::Debug for NamedStructNonExhaustive { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStructNonExhaustive") + .field("a", &self.a) + .finish_non_exhaustive() // should not warn here + } +} + +struct MultiExprDebugImpl { + a: u8, + b: String, +} + +// ok +impl fmt::Debug for MultiExprDebugImpl { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = formatter.debug_struct("MultiExprDebugImpl"); + f.field("a", &self.a); + f.finish() + } +} + +enum SingleVariantEnumUnnamed { + A(u8), +} + +// ok +impl fmt::Debug for SingleVariantEnumUnnamed { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + } + } +} + +enum MultiVariantEnum { + A(u8), + B { a: u8, b: String }, + C, +} + +impl fmt::Debug for MultiVariantEnum { + // match arm Self::B ignores `b` + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + Self::B { a, b } => formatter.debug_struct("B").field("a", &a).finish(), + Self::C => formatter.debug_struct("C").finish(), + } + } +} + +enum MultiVariantEnumOk { + A(u8), + B { a: u8, b: String }, + C, +} + +// ok +impl fmt::Debug for MultiVariantEnumOk { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + Self::B { a, b } => formatter.debug_struct("B").field("a", &a).field("b", &b).finish(), + Self::C => formatter.debug_struct("C").finish(), + } + } +} + +enum MultiVariantEnumNonExhaustive { + A(u8), + B { a: u8, b: String }, + C, +} + +// ok +impl fmt::Debug for MultiVariantEnumNonExhaustive { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + Self::B { a, b } => formatter.debug_struct("B").field("b", &b).finish_non_exhaustive(), + Self::C => formatter.debug_struct("C").finish(), + } + } +} + +enum MultiVariantRest { + A(u8), + B { a: u8, b: String }, + C, +} + +impl fmt::Debug for MultiVariantRest { + // `a` field ignored due to rest pattern + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish(), + Self::C => formatter.debug_struct("C").finish(), + } + } +} + +enum MultiVariantRestNonExhaustive { + A(u8), + B { a: u8, b: String }, + C, +} + +// ok +impl fmt::Debug for MultiVariantRestNonExhaustive { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish_non_exhaustive(), + Self::C => formatter.debug_struct("C").finish(), + } + } +} + +enum Wildcard { + A(u8), + B(String), +} + +// ok +impl fmt::Debug for Wildcard { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), + _ => todo!(), + } + } +} + +enum Empty {} + +// ok +impl fmt::Debug for Empty { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +#[derive(Debug)] +struct DerivedStruct { + a: u8, + b: i32, +} + +#[derive(Debug)] +enum DerivedEnum { + A(i32), + B { a: String }, +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1166846953 + +struct Inner { + a: usize, + b: usize, +} + +struct HasInner { + inner: Inner, +} + +impl HasInner { + fn get(&self) -> &Inner { + &self.inner + } +} + +impl fmt::Debug for HasInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.get(); + + f.debug_struct("HasInner") + .field("a", &inner.a) + .field("b", &inner.b) + .finish() + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1170306053 +struct Foo { + a: u8, + b: u8, +} + +impl fmt::Debug for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Foo").field("a", &self.a).field("b", &()).finish() + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175473620 +mod comment1175473620 { + use super::*; + + struct Inner { + a: usize, + b: usize, + } + struct Wrapper(Inner); + + impl Deref for Wrapper { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl fmt::Debug for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Wrapper") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175488757 +// PhantomData is an exception and does not need to be included +struct WithPD { + a: u8, + b: u8, + c: PhantomData, +} + +impl fmt::Debug for WithPD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WithPD") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +fn main() {} diff --git a/tests/ui/missing_fields_in_debug.stderr b/tests/ui/missing_fields_in_debug.stderr new file mode 100644 index 0000000000000..1dc7c0d65e5d2 --- /dev/null +++ b/tests/ui/missing_fields_in_debug.stderr @@ -0,0 +1,113 @@ +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:13:1 + | +LL | / impl fmt::Debug for NamedStruct1Ignored { +LL | | // unused field: hidden +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | formatter +... | +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:10:5 + | +LL | hidden: u32, + | ^^^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + = note: `-D clippy::missing-fields-in-debug` implied by `-D warnings` + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:31:1 + | +LL | / impl fmt::Debug for NamedStructMultipleIgnored { +LL | | // unused fields: hidden, hidden2, hidden4 +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | formatter +... | +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:25:5 + | +LL | hidden: u32, + | ^^^^^^^^^^^ +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:26:5 + | +LL | hidden2: String, + | ^^^^^^^^^^^^^^^ +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:28:5 + | +LL | hidden4: ((((u8), u16), u32), u64), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:92:1 + | +LL | / impl fmt::Debug for MultiExprDebugImpl { +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | let mut f = formatter.debug_struct("MultiExprDebugImpl"); +LL | | f.field("a", &self.a); +LL | | f.finish() +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:88:5 + | +LL | b: String, + | ^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:119:1 + | +LL | / impl fmt::Debug for MultiVariantEnum { +LL | | // match arm Self::B ignores `b` +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | match self { +... | +LL | | } +LL | | } + | |_^ + | +note: the field referenced by this binding is unused + --> $DIR/missing_fields_in_debug.rs:124:26 + | +LL | Self::B { a, b } => formatter.debug_struct("B").field("a", &a).finish(), + | ^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:170:1 + | +LL | / impl fmt::Debug for MultiVariantRest { +LL | | // `a` field ignored due to rest pattern +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | match self { +... | +LL | | } +LL | | } + | |_^ + | +note: more unused fields here due to rest pattern `..` + --> $DIR/missing_fields_in_debug.rs:175:13 + | +LL | Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish(), + | ^^^^^^^^^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: aborting due to 5 previous errors + From a859b0e6dfae12169d6a239e2a63aef8227e1dce Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Wed, 31 May 2023 23:08:49 +0200 Subject: [PATCH 568/806] don't lint enums, update note in lint description --- clippy_lints/src/missing_fields_in_debug.rs | 101 ++------------- tests/ui/missing_fields_in_debug.rs | 128 -------------------- tests/ui/missing_fields_in_debug.stderr | 42 +------ 3 files changed, 13 insertions(+), 258 deletions(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 389a8d04dc289..8332d638f92ab 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -2,22 +2,22 @@ use std::ops::ControlFlow; use clippy_utils::{ diagnostics::span_lint_and_then, - paths, + is_path_lang_item, paths, ty::match_type, visitors::{for_each_expr, Visitable}, }; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::Block; use rustc_hir::{ def::{DefKind, Res}, - Expr, ImplItemKind, MatchSource, Node, + Expr, ImplItemKind, LangItem, Node, }; -use rustc_hir::{Block, PatKind}; use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind}; use rustc_hir::{ImplItem, Item, VariantData}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; use rustc_middle::ty::TypeckResults; -use rustc_middle::ty::{EarlyBinder, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span, Symbol}; @@ -38,11 +38,12 @@ declare_clippy_lint! { /// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries /// to be on the conservative side and not lint in those cases in an attempt to prevent false positives. /// - /// This lint also does not look through function calls, so calling `.field(self.as_slice())` for example - /// does not consider fields used inside of `as_slice()` as used by the `Debug` impl. + /// This lint also does not look through function calls, so calling a function does not consider fields + /// used inside of that function as used by the `Debug` impl. /// /// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive` - /// method. + /// method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum, + /// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant. /// /// ### Example /// ```rust @@ -185,8 +186,7 @@ fn check_struct<'tcx>( .fields() .iter() .filter_map(|field| { - let EarlyBinder(field_ty) = cx.tcx.type_of(field.def_id); - if field_accesses.contains(&field.ident.name) || field_ty.is_phantom_data() { + if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { None } else { Some((field.span, "this field is unused")) @@ -201,82 +201,6 @@ fn check_struct<'tcx>( } } -/// Attempts to find unused fields in variants assuming that -/// the item is an enum. -/// -/// Currently, only simple cases are detected where the user -/// matches on `self` and calls `debug_struct` inside of the arms -fn check_enum<'tcx>( - cx: &LateContext<'tcx>, - typeck_results: &TypeckResults<'tcx>, - block: &'tcx Block<'tcx>, - self_ty: Ty<'tcx>, - item: &'tcx Item<'tcx>, -) { - let Some(arms) = for_each_expr(block, |expr| { - if let ExprKind::Match(val, arms, MatchSource::Normal) = expr.kind - && let match_ty = typeck_results.expr_ty_adjusted(val).peel_refs() - && match_ty == self_ty - { - ControlFlow::Break(arms) - } else { - ControlFlow::Continue(()) - } - }) else { - return; - }; - - let mut span_notes = Vec::new(); - - for arm in arms { - if !should_lint(cx, typeck_results, arm.body) { - continue; - } - - arm.pat.walk_always(|pat| match pat.kind { - PatKind::Wild => span_notes.push((pat.span, "unused field here due to wildcard `_`")), - PatKind::Tuple(_, rest) | PatKind::TupleStruct(.., rest) if rest.as_opt_usize().is_some() => { - span_notes.push((pat.span, "more unused fields here due to rest pattern `..`")); - }, - PatKind::Struct(.., true) => { - span_notes.push((pat.span, "more unused fields here due to rest pattern `..`")); - }, - _ => {}, - }); - - let mut field_accesses = FxHashSet::default(); - let mut check_field_access = |sym, expr| { - if !typeck_results.expr_ty(expr).is_phantom_data() { - arm.pat.each_binding(|_, _, _, pat_ident| { - if sym == pat_ident.name { - field_accesses.insert(pat_ident); - } - }); - } - }; - - for_each_expr(arm.body, |expr| { - if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind && let Some(segment) = path.segments.first() - { - check_field_access(segment.ident.name, expr); - } else if let Some(sym) = as_field_call(cx, typeck_results, expr) { - check_field_access(sym, expr); - } - ControlFlow::::Continue(()) - }); - - arm.pat.each_binding(|_, _, span, pat_ident| { - if !field_accesses.contains(&pat_ident) { - span_notes.push((span, "the field referenced by this binding is unused")); - } - }); - } - - if !span_notes.is_empty() { - report_lints(cx, item.span, span_notes); - } -} - impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { // is this an `impl Debug for X` block? @@ -301,10 +225,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let typeck_results = cx.tcx.typeck_body(*body_id) && should_lint(cx, typeck_results, block) { - match &self_item.kind { - ItemKind::Struct(data, _) => check_struct(cx, typeck_results, block, self_ty, item, data), - ItemKind::Enum(..) => check_enum(cx, typeck_results, block, self_ty, item), - _ => {} + // we intentionally only lint structs, see lint description + if let ItemKind::Struct(data, _) = &self_item.kind { + check_struct(cx, typeck_results, block, self_ty, item, data); } } } diff --git a/tests/ui/missing_fields_in_debug.rs b/tests/ui/missing_fields_in_debug.rs index 72b9e0e2aae5a..c156d394eceaa 100644 --- a/tests/ui/missing_fields_in_debug.rs +++ b/tests/ui/missing_fields_in_debug.rs @@ -97,140 +97,12 @@ impl fmt::Debug for MultiExprDebugImpl { } } -enum SingleVariantEnumUnnamed { - A(u8), -} - -// ok -impl fmt::Debug for SingleVariantEnumUnnamed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - } - } -} - -enum MultiVariantEnum { - A(u8), - B { a: u8, b: String }, - C, -} - -impl fmt::Debug for MultiVariantEnum { - // match arm Self::B ignores `b` - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - Self::B { a, b } => formatter.debug_struct("B").field("a", &a).finish(), - Self::C => formatter.debug_struct("C").finish(), - } - } -} - -enum MultiVariantEnumOk { - A(u8), - B { a: u8, b: String }, - C, -} - -// ok -impl fmt::Debug for MultiVariantEnumOk { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - Self::B { a, b } => formatter.debug_struct("B").field("a", &a).field("b", &b).finish(), - Self::C => formatter.debug_struct("C").finish(), - } - } -} - -enum MultiVariantEnumNonExhaustive { - A(u8), - B { a: u8, b: String }, - C, -} - -// ok -impl fmt::Debug for MultiVariantEnumNonExhaustive { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - Self::B { a, b } => formatter.debug_struct("B").field("b", &b).finish_non_exhaustive(), - Self::C => formatter.debug_struct("C").finish(), - } - } -} - -enum MultiVariantRest { - A(u8), - B { a: u8, b: String }, - C, -} - -impl fmt::Debug for MultiVariantRest { - // `a` field ignored due to rest pattern - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish(), - Self::C => formatter.debug_struct("C").finish(), - } - } -} - -enum MultiVariantRestNonExhaustive { - A(u8), - B { a: u8, b: String }, - C, -} - -// ok -impl fmt::Debug for MultiVariantRestNonExhaustive { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish_non_exhaustive(), - Self::C => formatter.debug_struct("C").finish(), - } - } -} - -enum Wildcard { - A(u8), - B(String), -} - -// ok -impl fmt::Debug for Wildcard { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::A(n) => formatter.debug_tuple("A").field(&n).finish(), - _ => todo!(), - } - } -} - -enum Empty {} - -// ok -impl fmt::Debug for Empty { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self {} - } -} - #[derive(Debug)] struct DerivedStruct { a: u8, b: i32, } -#[derive(Debug)] -enum DerivedEnum { - A(i32), - B { a: String }, -} - // https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1166846953 struct Inner { diff --git a/tests/ui/missing_fields_in_debug.stderr b/tests/ui/missing_fields_in_debug.stderr index 1dc7c0d65e5d2..ef9d02abab7db 100644 --- a/tests/ui/missing_fields_in_debug.stderr +++ b/tests/ui/missing_fields_in_debug.stderr @@ -69,45 +69,5 @@ LL | b: String, = help: consider including all fields in this `Debug` impl = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields -error: manual `Debug` impl does not include all fields - --> $DIR/missing_fields_in_debug.rs:119:1 - | -LL | / impl fmt::Debug for MultiVariantEnum { -LL | | // match arm Self::B ignores `b` -LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { -LL | | match self { -... | -LL | | } -LL | | } - | |_^ - | -note: the field referenced by this binding is unused - --> $DIR/missing_fields_in_debug.rs:124:26 - | -LL | Self::B { a, b } => formatter.debug_struct("B").field("a", &a).finish(), - | ^ - = help: consider including all fields in this `Debug` impl - = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields - -error: manual `Debug` impl does not include all fields - --> $DIR/missing_fields_in_debug.rs:170:1 - | -LL | / impl fmt::Debug for MultiVariantRest { -LL | | // `a` field ignored due to rest pattern -LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { -LL | | match self { -... | -LL | | } -LL | | } - | |_^ - | -note: more unused fields here due to rest pattern `..` - --> $DIR/missing_fields_in_debug.rs:175:13 - | -LL | Self::B { b, .. } => formatter.debug_struct("B").field("b", &b).finish(), - | ^^^^^^^^^^^^^^^^^ - = help: consider including all fields in this `Debug` impl - = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors From 8abb80326adfe676f418c7bf162d979c9f99d224 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 31 May 2023 22:54:01 -0400 Subject: [PATCH 569/806] Add portable-simd mention --- triagebot.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index c160c83cc9590..2758f1d1458b7 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -372,6 +372,14 @@ cc = ["@GuillaumeGomez"] message = "Some changes might have occurred in exhaustiveness checking" cc = ["@Nadrieril"] +[mentions."library/portable-simd"] +message = """ +Portable SIMD is developed in its own repository. If possible, consider \ +making this change to [rust-lang/portable-simd](https://github.com/rust-lang/portable-simd) \ +instead. +""" +cc = ["@calebzulawski"] + [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"] From 42450d2511f8f174dc7448d0e9839d4b76d64482 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 07:45:55 +0200 Subject: [PATCH 570/806] Add signature help for tuple patterns and expressions --- crates/ide/src/signature_help.rs | 418 ++++++++++++++++++++++++++++--- 1 file changed, 390 insertions(+), 28 deletions(-) diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 9a0529ec20fee..455b519f80b0b 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -15,8 +15,9 @@ use ide_db::{ use stdx::format_to; use syntax::{ algo, - ast::{self, HasArgList}, - match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize, + ast::{self, AstChildren, HasArgList}, + match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, + TextRange, TextSize, T, }; use crate::RootDatabase; @@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio } return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token); }, + ast::TuplePat(tuple_pat) => { + let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_tuple_pat(&sema, tuple_pat, token); + }, + ast::TupleExpr(tuple_expr) => { + let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_tuple_expr(&sema, tuple_expr, token); + }, _ => (), } } @@ -395,19 +410,16 @@ fn signature_help_for_tuple_struct_pat( pat: ast::TupleStructPat, token: SyntaxToken, ) -> Option { - let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_))); - let is_left_of_rest_pat = - rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); - + let path = pat.path()?; + let path_res = sema.resolve_path(&path)?; let mut res = SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter: None, }; - let db = sema.db; - let path_res = sema.resolve_path(&pat.path()?)?; + let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res { let en = variant.parent_enum(db); @@ -435,30 +447,72 @@ fn signature_help_for_tuple_struct_pat( _ => return None, } }; - let commas = pat - .syntax() - .children_with_tokens() - .filter_map(syntax::NodeOrToken::into_token) - .filter(|t| t.kind() == syntax::T![,]); - res.active_parameter = Some(if is_left_of_rest_pat { - commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count() - } else { - let n_commas = commas - .collect::>() - .into_iter() - .rev() - .take_while(|t| t.text_range().start() > token.text_range().start()) - .count(); - fields.len().saturating_sub(1).saturating_sub(n_commas) - }); + Some(signature_help_for_tuple_pat_ish( + db, + res, + pat.syntax(), + token, + pat.fields(), + fields.into_iter().map(|it| it.ty(db)), + )) +} + +fn signature_help_for_tuple_pat( + sema: &Semantics<'_, RootDatabase>, + pat: ast::TuplePat, + token: SyntaxToken, +) -> Option { + let db = sema.db; + let field_pats = pat.fields(); + let pat = pat.into(); + let ty = sema.type_of_pat(&pat)?; + let fields = ty.original.tuple_fields(db); + + Some(signature_help_for_tuple_pat_ish( + db, + SignatureHelp { + doc: None, + signature: String::from('('), + parameters: vec![], + active_parameter: None, + }, + pat.syntax(), + token, + field_pats, + fields.into_iter(), + )) +} + +fn signature_help_for_tuple_expr( + sema: &Semantics<'_, RootDatabase>, + expr: ast::TupleExpr, + token: SyntaxToken, +) -> Option { + let active_parameter = Some( + expr.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]) + .take_while(|t| t.text_range().start() <= token.text_range().start()) + .count(), + ); + let db = sema.db; + let mut res = SignatureHelp { + doc: None, + signature: String::from('('), + parameters: vec![], + active_parameter, + }; + let expr = sema.type_of_expr(&expr.into())?; + let fields = expr.original.tuple_fields(db); let mut buf = String::new(); - for ty in fields.into_iter().map(|it| it.ty(db)) { + for ty in fields { format_to!(buf, "{}", ty.display_truncated(db, Some(20))); res.push_call_param(&buf); buf.clear(); } - res.signature.push_str(")"); + res.signature.push(')'); Some(res) } @@ -470,8 +524,8 @@ fn signature_help_for_record_( token: SyntaxToken, ) -> Option { let active_parameter = field_list_children - .filter_map(syntax::NodeOrToken::into_token) - .filter(|t| t.kind() == syntax::T![,]) + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]) .take_while(|t| t.text_range().start() <= token.text_range().start()) .count(); @@ -542,6 +596,46 @@ fn signature_help_for_record_( Some(res) } +fn signature_help_for_tuple_pat_ish( + db: &RootDatabase, + mut res: SignatureHelp, + pat: &SyntaxNode, + token: SyntaxToken, + mut field_pats: AstChildren, + fields: impl ExactSizeIterator, +) -> SignatureHelp { + let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_))); + let is_left_of_rest_pat = + rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); + + let commas = pat + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]); + + res.active_parameter = { + Some(if is_left_of_rest_pat { + commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count() + } else { + let n_commas = commas + .collect::>() + .into_iter() + .rev() + .take_while(|t| t.text_range().start() > token.text_range().start()) + .count(); + fields.len().saturating_sub(1).saturating_sub(n_commas) + }) + }; + + let mut buf = String::new(); + for ty in fields { + format_to!(buf, "{}", ty.display_truncated(db, Some(20))); + res.push_call_param(&buf); + buf.clear(); + } + res.signature.push_str(")"); + res +} #[cfg(test)] mod tests { use std::iter; @@ -1851,4 +1945,272 @@ fn main() { "#]], ); } + + #[test] + fn test_tuple_expr_free() { + check( + r#" +fn main() { + (0$0, 1, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + ($0 1, 3); +} +"#, + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + (1, 3 $0); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + (1, 3 $0,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + } + + #[test] + fn test_tuple_expr_expected() { + // FIXME: Seems like we discard valuable results in typeck here + check( + r#" +fn main() { + let _: (&str, u32, u32)= ($0, 1, 3); +} +"#, + expect![""], + ); + check( + r#" +fn main() { + let _: (&str, u32, u32, u32)= ($0, 1, 3); +} +"#, + expect![""], + ); + check( + r#" +fn main() { + let _: (&str, u32, u32)= ($0, 1, 3, 5); +} +"#, + expect![""], + ); + } + + #[test] + fn test_tuple_pat_free() { + // FIXME: Seems like we discard valuable results in typeck here + check( + r#" +fn main() { + let (0$0, 1, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0 1, 3); +} +"#, + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0); +} +"#, + // FIXME: This is wrong, this should not mark the last as active + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + } + + #[test] + fn test_tuple_pat_expected() { + check( + r#" +fn main() { + let (0$0, 1, 3): (i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0 1, 3): (i32, i32, i32); +} +"#, + // FIXME: tuple pat should be of size 3 ideally + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0): (i32,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..): (i32, i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32, i32) + --- ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0): (i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32) + --- --- ^^^ + "#]], + ); + } + #[test] + fn test_tuple_pat_expected_inferred() { + check( + r#" +fn main() { + let (0$0, 1, 3) = (1, 2 ,3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0 1, 3) = (1, 2, 3); +} +"#, + // FIXME: tuple pat should be of size 3 ideally + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0) = (1,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..) = (1, 2, 3, 4); +} +"#, + expect![[r#" + (i32, i32, i32, i32) + --- ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0) = (1, 2, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + --- --- ^^^ + "#]], + ); + } } From 21bc5cded401c04316ce52697533cf928c007c63 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 1 Jun 2023 06:14:06 +0000 Subject: [PATCH 571/806] Rename `impl_defaultness` to `defaultness` --- compiler/rustc_hir_analysis/src/check/check.rs | 4 ++-- compiler/rustc_hir_analysis/src/collect.rs | 2 +- compiler/rustc_hir_analysis/src/collect/type_of.rs | 2 +- .../src/infer/error_reporting/note_and_explain.rs | 2 +- .../rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +++++----- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 5 +++-- .../rustc_middle/src/traits/specialization_graph.rs | 2 +- compiler/rustc_middle/src/ty/assoc.rs | 2 +- compiler/rustc_privacy/src/lib.rs | 4 ++-- compiler/rustc_trait_selection/src/traits/util.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 8 ++++---- compiler/rustc_ty_utils/src/instance.rs | 2 +- compiler/rustc_ty_utils/src/ty.rs | 6 +++--- src/librustdoc/clean/mod.rs | 6 +++--- src/librustdoc/passes/collect_intra_doc_links.rs | 2 +- src/tools/clippy/clippy_lints/src/missing_inline.rs | 2 +- 18 files changed, 33 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3b2c052e8f459..f6350995eaff0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -704,7 +704,7 @@ pub(super) fn check_specialization_validity<'tcx>( // grandparent. In that case, if parent is a `default impl`, inherited items use the // "defaultness" from the grandparent, else they are final. None => { - if tcx.impl_defaultness(parent_impl.def_id()).is_default() { + if tcx.defaultness(parent_impl.def_id()).is_default() { None } else { Some(Err(parent_impl.def_id())) @@ -803,7 +803,7 @@ fn check_impl_items_against_trait<'tcx>( .as_ref() .is_some_and(|node_item| node_item.item.defaultness(tcx).has_value()); - if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + if !is_implemented && tcx.defaultness(impl_id).is_final() { missing_items.push(tcx.associated_item(trait_item_id)); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 2f7d465839c4c..c7b9fc9a697f4 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -941,7 +941,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { match item { Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => { - if !tcx.impl_defaultness(item.id.owner_id).has_value() { + if !tcx.defaultness(item.id.owner_id).has_value() { tcx.sess.emit_err(errors::FunctionNotHaveDefaultImplementation { span: item.span, note_span: attr_span, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 65ab00fda817a..e6fadd6cf4a85 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -435,7 +435,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { - if in_trait && !tcx.impl_defaultness(owner).has_value() { + if in_trait && !tcx.defaultness(owner).has_value() { span_bug!( tcx.def_span(def_id), "tried to get type of this RPITIT with no definition" diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index d1f110472c927..9a8fac0fc1be3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -586,7 +586,7 @@ fn foo(&self) -> Self::T { String::new() } // FIXME: account for returning some type in a trait fn impl that has // an assoc type as a return type (#72076). if let hir::Defaultness::Default { has_value: true } = - tcx.impl_defaultness(item.id.owner_id) + tcx.defaultness(item.id.owner_id) { let assoc_ty = tcx.type_of(item.id.owner_id).subst_identity(); if self.infcx.can_eq(param_env, assoc_ty, found) { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a15307e4345c1..243e4f5b65907 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -231,7 +231,7 @@ provide! { tcx, def_id, other, cdata, opt_def_kind => { table_direct } impl_parent => { table } impl_polarity => { table_direct } - impl_defaultness => { table_direct } + defaultness => { table_direct } constness => { table_direct } coerce_unsized_info => { table } mir_const_qualif => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6ceb61e793e53..05c9f154574b2 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1437,8 +1437,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; - let impl_defaultness = tcx.impl_defaultness(def_id.expect_local()); - self.tables.impl_defaultness.set_some(def_id.index, impl_defaultness); + let defaultness = tcx.defaultness(def_id.expect_local()); + self.tables.defaultness.set_some(def_id.index, defaultness); let trait_item = tcx.associated_item(def_id); self.tables.assoc_container.set_some(def_id.index, trait_item.container); @@ -1466,8 +1466,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_info_for_impl_item({:?})", def_id); let tcx = self.tcx; - let defaultness = self.tcx.impl_defaultness(def_id.expect_local()); - self.tables.impl_defaultness.set_some(def_id.index, defaultness); + let defaultness = self.tcx.defaultness(def_id.expect_local()); + self.tables.defaultness.set_some(def_id.index, defaultness); let impl_item = self.tcx.associated_item(def_id); self.tables.assoc_container.set_some(def_id.index, impl_item.container); @@ -1653,7 +1653,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ); } hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => { - self.tables.impl_defaultness.set_some(def_id.index, *defaultness); + self.tables.defaultness.set_some(def_id.index, *defaultness); self.tables.constness.set_some(def_id.index, *constness); self.tables.impl_polarity.set_some(def_id.index, self.tcx.impl_polarity(def_id)); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2da888f4468c0..e9b67c70c477a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -420,7 +420,7 @@ define_tables! { impl_parent: Table, impl_polarity: Table, constness: Table, - impl_defaultness: Table, + defaultness: Table, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table>, mir_const_qualif: Table>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0b31c9bbf8149..45e0b498fac67 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1475,8 +1475,9 @@ rustc_queries! { desc { "getting traits in scope at a block" } } - query impl_defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + /// Returns whether the impl or associated function has the `default` keyword. + query defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } separate_provide_extern feedable } diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index c016f72275099..dc2cd20356094 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -228,7 +228,7 @@ impl<'tcx> Ancestors<'tcx> { if let Some(item) = node.item(tcx, trait_item_def_id) { if finalizing_node.is_none() { let is_specializable = item.defaultness(tcx).is_default() - || tcx.impl_defaultness(node.def_id()).is_default(); + || tcx.defaultness(node.def_id()).is_default(); if !is_specializable { finalizing_node = Some(node); diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 090b769323add..cce609c261e8d 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -48,7 +48,7 @@ impl AssocItem { /// /// [`type_of`]: crate::ty::TyCtxt::type_of pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness { - tcx.impl_defaultness(self.def_id) + tcx.defaultness(self.def_id) } #[inline] diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 65dfdf31e5457..0197b99c7db19 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -751,7 +751,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { reach.generics().predicates(); if trait_item_ref.kind == AssocItemKind::Type - && !tcx.impl_defaultness(trait_item_ref.id.owner_id).has_value() + && !tcx.defaultness(trait_item_ref.id.owner_id).has_value() { // No type to visit. } else { @@ -1927,7 +1927,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> { let (check_ty, is_assoc_ty) = match assoc_item_kind { AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false), - AssocItemKind::Type => (self.tcx.impl_defaultness(def_id).has_value(), true), + AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true), }; check.in_assoc_ty = is_assoc_ty; check.generics().predicates(); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index e2c9c62512e7e..537405b7eb91a 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -295,7 +295,7 @@ pub fn future_trait_ref_and_outputs<'tcx>( pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { assoc_item.defaultness(tcx).is_final() - && tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final() + && tcx.defaultness(assoc_item.container_id(tcx)).is_final() } pub enum TupleArgumentsFlag { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 57b4183d33671..658ab03c0f48c 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -297,8 +297,8 @@ fn associated_type_for_impl_trait_in_trait( // Copy visility of the containing function. trait_assoc_ty.visibility(tcx.visibility(fn_def_id)); - // Copy impl_defaultness of the containing function. - trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id)); + // Copy defaultness of the containing function. + trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id)); // Copy type_of of the opaque. trait_assoc_ty.type_of(ty::EarlyBinder::bind(tcx.mk_opaque( @@ -393,8 +393,8 @@ fn associated_type_for_impl_trait_in_impl( // Copy visility of the containing function. impl_assoc_ty.visibility(tcx.visibility(impl_fn_def_id)); - // Copy impl_defaultness of the containing function. - impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id)); + // Copy defaultness of the containing function. + impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id)); // Copy generics_of the trait's associated item but the impl as the parent. // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) resolves to the trait instead of the impl diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 36a20c78fcc7b..dd911239ac451 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -204,7 +204,7 @@ fn resolve_associated_item<'tcx>( } else { // All other methods are default methods of the `Future` trait. // (this assumes that `ImplSource::Future` is only used for methods on `Future`) - debug_assert!(tcx.impl_defaultness(trait_item_id).has_value()); + debug_assert!(tcx.defaultness(trait_item_id).has_value()); Some(Instance::new(trait_item_id, rcvr_substs)) } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 12ad8b0842f5e..b367d81a2608e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -75,13 +75,13 @@ fn sized_constraint_for_ty<'tcx>( result } -fn impl_defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { +fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { match tcx.hir().get_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness, hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, node => { - bug!("`impl_defaultness` called on {:?}", node); + bug!("`defaultness` called on {:?}", node); } } } @@ -574,7 +574,7 @@ pub fn provide(providers: &mut Providers) { param_env_reveal_all_normalized, instance_def_size_estimate, issue33140_self_ty, - impl_defaultness, + defaultness, unsizing_params_for_adt, ..*providers }; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5fd867189fd71..b84a9cf369c1a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1224,7 +1224,7 @@ pub(crate) fn clean_impl_item<'tcx>( } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); - let defaultness = cx.tcx.impl_defaultness(impl_.owner_id); + let defaultness = cx.tcx.defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { @@ -1258,7 +1258,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let provided = match assoc_item.container { ty::ImplContainer => true, - ty::TraitContainer => tcx.impl_defaultness(assoc_item.def_id).has_value(), + ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(), }; if provided { AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) @@ -1440,7 +1440,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } generics.where_predicates = where_predicates; - if tcx.impl_defaultness(assoc_item.def_id).has_value() { + if tcx.defaultness(assoc_item.def_id).has_value() { AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 2b0fef267e9a2..c31ba3304b032 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -205,7 +205,7 @@ impl UrlFragment { &UrlFragment::Item(def_id) => { let kind = match tcx.def_kind(def_id) { DefKind::AssocFn => { - if tcx.impl_defaultness(def_id).has_value() { + if tcx.defaultness(def_id).has_value() { "method." } else { "tymethod." diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 5a459548153aa..a41d5a9ce8d2f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { match tit_.kind { hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, hir::TraitItemKind::Fn(..) => { - if cx.tcx.impl_defaultness(tit.id.owner_id).has_value() { + if cx.tcx.defaultness(tit.id.owner_id).has_value() { // trait method with default body needs inline in case // an impl is not provided let desc = "a default trait method"; From 7d1bf7023d7c11c20bfc35c9b9f4bfa027858d65 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 08:40:50 +0200 Subject: [PATCH 572/806] Recover from leading comma in tuple pat and expr --- crates/ide/src/signature_help.rs | 17 +++++++++--- crates/parser/src/grammar/expressions/atom.rs | 10 +++++++ crates/parser/src/grammar/patterns.rs | 10 +++++++ .../err/0019_tuple_expr_leading_comma.rast | 24 +++++++++++++++++ .../err/0019_tuple_expr_leading_comma.rs | 3 +++ .../err/0020_tuple_pat_leading_comma.rast | 26 +++++++++++++++++++ .../err/0020_tuple_pat_leading_comma.rs | 3 +++ 7 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast create mode 100644 crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs create mode 100644 crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast create mode 100644 crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 455b519f80b0b..9bd9a948e7f72 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -2003,7 +2003,10 @@ fn main() { let _: (&str, u32, u32)= ($0, 1, 3); } "#, - expect![""], + expect![[r#" + (&str, u32) + ^^^^ --- + "#]], ); check( r#" @@ -2011,7 +2014,10 @@ fn main() { let _: (&str, u32, u32, u32)= ($0, 1, 3); } "#, - expect![""], + expect![[r#" + (&str, u32) + ^^^^ --- + "#]], ); check( r#" @@ -2019,7 +2025,10 @@ fn main() { let _: (&str, u32, u32)= ($0, 1, 3, 5); } "#, - expect![""], + expect![[r#" + (&str, u32, u32) + ^^^^ --- --- + "#]], ); } @@ -2111,7 +2120,7 @@ fn main() { check( r#" fn main() { - let ($0 1, 3): (i32, i32, i32); + let ($0, 1, 3): (i32, i32, i32); } "#, // FIXME: tuple pat should be of size 3 ideally diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 3cf9c4dd4b07a..d8553d3f95377 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -184,6 +184,16 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_comma = false; let mut saw_expr = false; + + // test_err tuple_expr_leading_comma + // fn foo() { + // (,); + // } + if p.eat(T![,]) { + p.error("expected expression"); + saw_comma = true; + } + while !p.at(EOF) && !p.at(T![')']) { saw_expr = true; diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 4801732101fc9..39ded41bb2413 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -413,6 +413,16 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker { let mut has_comma = false; let mut has_pat = false; let mut has_rest = false; + + // test_err tuple_pat_leading_comma + // fn foo() { + // let (,); + // } + if p.eat(T![,]) { + p.error("expected pattern"); + has_comma = true; + } + while !p.at(EOF) && !p.at(T![')']) { has_pat = true; if !p.at_ts(PAT_TOP_FIRST) { diff --git a/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast b/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast new file mode 100644 index 0000000000000..3fbc0da4002e4 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast @@ -0,0 +1,24 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + TUPLE_EXPR + L_PAREN "(" + COMMA "," + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 17: expected expression diff --git a/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs b/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs new file mode 100644 index 0000000000000..12fab59a77656 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs @@ -0,0 +1,3 @@ +fn foo() { + (,); +} diff --git a/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast b/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast new file mode 100644 index 0000000000000..9c8837292d289 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast @@ -0,0 +1,26 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + TUPLE_PAT + L_PAREN "(" + COMMA "," + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 21: expected pattern diff --git a/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs b/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs new file mode 100644 index 0000000000000..de168521e1d3f --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs @@ -0,0 +1,3 @@ +fn foo() { + let (,); +} From 0e2820283261183972b016d86a92144a3f8472e5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 08:56:40 +0200 Subject: [PATCH 573/806] Insert missing expr/pat for leading comma tuples --- crates/hir-def/src/body/lower.rs | 40 +++++++++++++++++++++++++++----- crates/ide/src/signature_help.rs | 35 +++++++++++++++++----------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index acc9943481a8c..ebe05afca6a81 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -542,9 +542,18 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) } ast::Expr::TupleExpr(e) => { - let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect(); + let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect(); + // if there is a leading comma, the user is most likely to type out a leading expression + // so we insert a missing expression at the beginning for IDE features + if comma_follows_token(e.l_paren_token()) { + exprs.insert(0, self.missing_expr()); + } + self.alloc_expr( - Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr }, + Expr::Tuple { + exprs: exprs.into_boxed_slice(), + is_assignee_expr: self.is_lowering_assignee_expr, + }, syntax_ptr, ) } @@ -1180,7 +1189,11 @@ impl ExprCollector<'_> { ast::Pat::TupleStructPat(p) => { let path = p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); - let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list); + let (args, ellipsis) = self.collect_tuple_pat( + p.fields(), + comma_follows_token(p.l_paren_token()), + binding_list, + ); Pat::TupleStruct { path, args, ellipsis } } ast::Pat::RefPat(p) => { @@ -1199,7 +1212,11 @@ impl ExprCollector<'_> { } ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list), ast::Pat::TuplePat(p) => { - let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list); + let (args, ellipsis) = self.collect_tuple_pat( + p.fields(), + comma_follows_token(p.l_paren_token()), + binding_list, + ); Pat::Tuple { args, ellipsis } } ast::Pat::WildcardPat(_) => Pat::Wild, @@ -1323,18 +1340,24 @@ impl ExprCollector<'_> { fn collect_tuple_pat( &mut self, args: AstChildren, + has_leading_comma: bool, binding_list: &mut BindingList, ) -> (Box<[PatId]>, Option) { // Find the location of the `..`, if there is one. Note that we do not // consider the possibility of there being multiple `..` here. let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); // We want to skip the `..` pattern here, since we account for it above. - let args = args + let mut args: Vec<_> = args .filter(|p| !matches!(p, ast::Pat::RestPat(_))) .map(|p| self.collect_pat(p, binding_list)) .collect(); + // if there is a leading comma, the user is most likely to type out a leading pattern + // so we insert a missing pattern at the beginning for IDE features + if has_leading_comma { + args.insert(0, self.missing_pat()); + } - (args, ellipsis) + (args.into_boxed_slice(), ellipsis) } // endregion: patterns @@ -1493,3 +1516,8 @@ impl ExprCollector<'_> { self.body.labels.alloc(label) } } + +fn comma_follows_token(t: Option) -> bool { + (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() + .map_or(false, |it| it.kind() == syntax::T![,]) +} diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 9bd9a948e7f72..7795be54e264c 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -1996,7 +1996,6 @@ fn main() { #[test] fn test_tuple_expr_expected() { - // FIXME: Seems like we discard valuable results in typeck here check( r#" fn main() { @@ -2004,19 +2003,20 @@ fn main() { } "#, expect![[r#" - (&str, u32) - ^^^^ --- + (&str, u32, u32) + ^^^^ --- --- "#]], ); + // FIXME: Should typeck report a 4-ary tuple for the expression here? check( r#" fn main() { - let _: (&str, u32, u32, u32)= ($0, 1, 3); + let _: (&str, u32, u32, u32) = ($0, 1, 3); } "#, expect![[r#" - (&str, u32) - ^^^^ --- + (&str, u32, u32) + ^^^^ --- --- "#]], ); check( @@ -2026,15 +2026,25 @@ fn main() { } "#, expect![[r#" - (&str, u32, u32) - ^^^^ --- --- + (&str, u32, u32, i32) + ^^^^ --- --- --- "#]], ); } #[test] fn test_tuple_pat_free() { - // FIXME: Seems like we discard valuable results in typeck here + check( + r#" +fn main() { + let ($0, 1, 3); +} +"#, + expect![[r#" + ({unknown}, i32, i32) + ^^^^^^^^^ --- --- + "#]], + ); check( r#" fn main() { @@ -2123,10 +2133,9 @@ fn main() { let ($0, 1, 3): (i32, i32, i32); } "#, - // FIXME: tuple pat should be of size 3 ideally expect![[r#" - (i32, i32) - ^^^ --- + (i32, i32, i32) + ^^^ --- --- "#]], ); check( @@ -2182,7 +2191,7 @@ fn main() { let ($0 1, 3) = (1, 2, 3); } "#, - // FIXME: tuple pat should be of size 3 ideally + // FIXME: Should typeck report a 3-ary tuple for the pattern here? expect![[r#" (i32, i32) ^^^ --- From 32cc106af3486a6f390b7428dfc2414e7cbccb0c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 31 May 2023 23:58:20 -0700 Subject: [PATCH 574/806] Lower unchecked_{div, rem} to BinOp::{Div, Rem} --- .../src/lower_intrinsics.rs | 51 +++++++++------- ...r_intrinsics.align_of.LowerIntrinsics.diff | 2 +- ...wer_intrinsics.assume.LowerIntrinsics.diff | 2 +- ...trinsics.discriminant.LowerIntrinsics.diff | 14 ++--- ...f_copy_nonoverlapping.LowerIntrinsics.diff | 2 +- ...wer_intrinsics.forget.LowerIntrinsics.diff | 2 +- ..._intrinsics.non_const.LowerIntrinsics.diff | 2 +- ...insics.option_payload.LowerIntrinsics.diff | 4 +- ...intrinsics.ptr_offset.LowerIntrinsics.diff | 2 +- ...ad_via_copy_primitive.LowerIntrinsics.diff | 2 +- ..._via_copy_uninhabited.LowerIntrinsics.diff | 2 +- tests/mir-opt/lower_intrinsics.rs | 6 ++ ...er_intrinsics.size_of.LowerIntrinsics.diff | 2 +- ...s.transmute_inhabited.LowerIntrinsics.diff | 2 +- ...ics.transmute_ref_dst.LowerIntrinsics.diff | 2 +- ...te_to_box_uninhabited.LowerIntrinsics.diff | 2 +- ...te_to_mut_uninhabited.LowerIntrinsics.diff | 2 +- ...te_to_ref_uninhabited.LowerIntrinsics.diff | 2 +- ...transmute_uninhabited.LowerIntrinsics.diff | 2 +- ..._intrinsics.unchecked.LowerIntrinsics.diff | 60 +++++++++++++++++++ ...ntrinsics.unreachable.LowerIntrinsics.diff | 2 +- ...rinsics.with_overflow.LowerIntrinsics.diff | 6 +- ...write_via_move_string.LowerIntrinsics.diff | 2 +- 23 files changed, 123 insertions(+), 52 deletions(-) create mode 100644 tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index dae01e41e5f3d..3a7d58f712568 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -82,30 +82,35 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { drop(args); terminator.kind = TerminatorKind::Goto { target }; } - sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { - if let Some(target) = *target { - let lhs; - let rhs; - { - let mut args = args.drain(..); - lhs = args.next().unwrap(); - rhs = args.next().unwrap(); - } - let bin_op = match intrinsic_name { - sym::wrapping_add => BinOp::Add, - sym::wrapping_sub => BinOp::Sub, - sym::wrapping_mul => BinOp::Mul, - _ => bug!("unexpected intrinsic"), - }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; + sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::unchecked_div + | sym::unchecked_rem => { + let target = target.unwrap(); + let lhs; + let rhs; + { + let mut args = args.drain(..); + lhs = args.next().unwrap(); + rhs = args.next().unwrap(); } + let bin_op = match intrinsic_name { + sym::wrapping_add => BinOp::Add, + sym::wrapping_sub => BinOp::Sub, + sym::wrapping_mul => BinOp::Mul, + sym::unchecked_div => BinOp::Div, + sym::unchecked_rem => BinOp::Rem, + _ => bug!("unexpected intrinsic"), + }; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { if let Some(target) = *target { diff --git a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff index dd742d87a2955..3530f4a807f2c 100644 --- a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff @@ -7,7 +7,7 @@ bb0: { - _0 = std::intrinsics::min_align_of::() -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:21:5: 21:40 +- // + span: $DIR/lower_intrinsics.rs:27:5: 27:40 - // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::min_align_of::}, val: Value() } + _0 = AlignOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 diff --git a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff index e95c3f9bcc707..158ce62e209c2 100644 --- a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff @@ -11,7 +11,7 @@ StorageLive(_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - _1 = std::intrinsics::assume(const true) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:106:9: 106:32 +- // + span: $DIR/lower_intrinsics.rs:112:9: 112:32 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(bool) {std::intrinsics::assume}, val: Value() } + assume(const true); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 diff --git a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff index 1f03b7b0baf4b..6fc9616d85540 100644 --- a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff @@ -31,7 +31,7 @@ _3 = &(*_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:42: +1:44 - _2 = discriminant_value::(move _3) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:82:5: 82:41 +- // + span: $DIR/lower_intrinsics.rs:88:5: 88:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a T) -> ::Discriminant {discriminant_value::}, val: Value() } + _2 = discriminant((*_3)); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 @@ -46,13 +46,13 @@ StorageLive(_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _19 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:83:42: 83:44 + // + span: $DIR/lower_intrinsics.rs:89:42: 89:44 // + literal: Const { ty: &i32, val: Unevaluated(discriminant, [T], Some(promoted[2])) } _7 = &(*_19); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _6 = &(*_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 - _5 = discriminant_value::(move _6) -> [return: bb2, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:83:5: 83:41 +- // + span: $DIR/lower_intrinsics.rs:89:5: 89:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a i32) -> ::Discriminant {discriminant_value::}, val: Value() } + _5 = discriminant((*_6)); // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 + goto -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 @@ -67,13 +67,13 @@ StorageLive(_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _18 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:84:42: 84:45 + // + span: $DIR/lower_intrinsics.rs:90:42: 90:45 // + literal: Const { ty: &(), val: Unevaluated(discriminant, [T], Some(promoted[1])) } _11 = &(*_18); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _10 = &(*_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 - _9 = discriminant_value::<()>(move _10) -> [return: bb3, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:84:5: 84:41 +- // + span: $DIR/lower_intrinsics.rs:90:5: 90:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a ()) -> <() as DiscriminantKind>::Discriminant {discriminant_value::<()>}, val: Value() } + _9 = discriminant((*_10)); // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 + goto -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 @@ -88,13 +88,13 @@ StorageLive(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _17 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:85:42: 85:47 + // + span: $DIR/lower_intrinsics.rs:91:42: 91:47 // + literal: Const { ty: &E, val: Unevaluated(discriminant, [T], Some(promoted[0])) } _15 = &(*_17); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _14 = &(*_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 - _13 = discriminant_value::(move _14) -> [return: bb4, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:85:5: 85:41 +- // + span: $DIR/lower_intrinsics.rs:91:5: 91:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a E) -> ::Discriminant {discriminant_value::}, val: Value() } + _13 = discriminant((*_14)); // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 + goto -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff index a54e9a9320ee1..5b870ccf5ee28 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff @@ -49,7 +49,7 @@ StorageDead(_9); // scope 3 at $DIR/lower_intrinsics.rs:+4:90: +4:91 - _3 = copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:99:9: 99:28 +- // + span: $DIR/lower_intrinsics.rs:105:9: 105:28 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, *mut i32, usize) {copy_nonoverlapping::}, val: Value() } + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 + goto -> bb1; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 diff --git a/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff index bfb000ccdb5c6..582a79f48d8c8 100644 --- a/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff @@ -11,7 +11,7 @@ _2 = move _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:30: +1:31 - _0 = std::intrinsics::forget::(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:26:5: 26:29 +- // + span: $DIR/lower_intrinsics.rs:32:5: 32:29 - // + literal: Const { ty: extern "rust-intrinsic" fn(T) {std::intrinsics::forget::}, val: Value() } + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 diff --git a/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff index 64d82907c7e36..81ad97077b428 100644 --- a/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff @@ -13,7 +13,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+2:9: +2:18 _1 = std::intrinsics::size_of::; // scope 0 at $DIR/lower_intrinsics.rs:+2:21: +2:51 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:37:21: 37:51 + // + span: $DIR/lower_intrinsics.rs:43:21: 43:51 // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::}, val: Value() } StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14 _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14 diff --git a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff index b022e2ba42bb8..edc66e2c75ce4 100644 --- a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff @@ -24,7 +24,7 @@ _4 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:55: +2:56 - _3 = option_payload_ptr::(move _4) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:137:18: 137:54 +- // + span: $DIR/lower_intrinsics.rs:143:18: 143:54 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option) -> *const usize {option_payload_ptr::}, val: Value() } + _3 = &raw const (((*_4) as Some).0: usize); // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 @@ -37,7 +37,7 @@ _6 = &raw const (*_2); // scope 2 at $DIR/lower_intrinsics.rs:+3:55: +3:56 - _5 = option_payload_ptr::(move _6) -> [return: bb2, unwind unreachable]; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:138:18: 138:54 +- // + span: $DIR/lower_intrinsics.rs:144:18: 144:54 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option) -> *const String {option_payload_ptr::}, val: Value() } + _5 = &raw const (((*_6) as Some).0: std::string::String); // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 + goto -> bb2; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 diff --git a/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff index 60a1dd0ba7d09..1760efe77d98f 100644 --- a/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff @@ -15,7 +15,7 @@ _4 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:33: +1:34 - _0 = offset::<*const i32, isize>(move _3, move _4) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:144:5: 144:29 +- // + span: $DIR/lower_intrinsics.rs:150:5: 150:29 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, isize) -> *const i32 {offset::<*const i32, isize>}, val: Value() } + _0 = Offset(move _3, move _4); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 diff --git a/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff index 5805df48f544f..8583766348a6e 100644 --- a/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+1:46: +1:47 - _0 = read_via_copy::(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:119:14: 119:45 +- // + span: $DIR/lower_intrinsics.rs:125:14: 125:45 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32) -> i32 {read_via_copy::}, val: Value() } + _0 = (*_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 diff --git a/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff index 95b2ec49d8076..f64bc9dcf620c 100644 --- a/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+1:46: +1:47 - _0 = read_via_copy::(move _2) -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:124:14: 124:45 +- // + span: $DIR/lower_intrinsics.rs:130:14: 130:45 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Never) -> Never {read_via_copy::}, val: Value() } + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 } diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 0ca88a42e3fd0..30b5c78e647c8 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -11,6 +11,12 @@ pub fn wrapping(a: i32, b: i32) { let _z = core::intrinsics::wrapping_mul(a, b); } +// EMIT_MIR lower_intrinsics.unchecked.LowerIntrinsics.diff +pub unsafe fn unchecked(a: i32, b: i32) { + let _x = core::intrinsics::unchecked_div(a, b); + let _y = core::intrinsics::unchecked_rem(a, b); +} + // EMIT_MIR lower_intrinsics.size_of.LowerIntrinsics.diff pub fn size_of() -> usize { core::intrinsics::size_of::() diff --git a/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff index 27e8accea8be5..a880df6a5c236 100644 --- a/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff @@ -7,7 +7,7 @@ bb0: { - _0 = std::intrinsics::size_of::() -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:16:5: 16:35 +- // + span: $DIR/lower_intrinsics.rs:22:5: 22:35 - // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::}, val: Value() } + _0 = SizeOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 diff --git a/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff index 1b3b7685185bc..cde7c64c57a56 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 - _0 = transmute::(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:43:14: 43:33 +- // + span: $DIR/lower_intrinsics.rs:49:14: 49:33 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(std::cmp::Ordering) -> i8 {transmute::}, val: Value() } + _0 = move _2 as i8 (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 diff --git a/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff index c6a7d2287e75d..6fc0f3d3e3fee 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 - _0 = transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:53:14: 53:33 +- // + span: $DIR/lower_intrinsics.rs:59:14: 59:33 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&T) -> *const T {transmute::<&T, *const T>}, val: Value() } + _0 = move _2 as *const T (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff index aa5d9619d10a9..e6887a382a2d6 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:70:25: 70:44 +- // + span: $DIR/lower_intrinsics.rs:76:25: 76:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> Box {transmute::>}, val: Value() } + _1 = const 1_usize as std::boxed::Box (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff index 5fafd45fe8526..b2a44b7c56114 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:64:25: 64:44 +- // + span: $DIR/lower_intrinsics.rs:70:25: 70:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &mut Never {transmute::}, val: Value() } + _1 = const 1_usize as &mut Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff index 08dead1321194..c49d3aeff70b6 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:58:21: 58:40 +- // + span: $DIR/lower_intrinsics.rs:64:21: 64:40 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &Never {transmute::}, val: Value() } + _1 = const 1_usize as &Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 diff --git a/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff index f0b76127dd5e6..06759d74a3205 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:47: +1:48 - _0 = transmute::<(), Never>(move _2) -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:48:14: 48:46 +- // + span: $DIR/lower_intrinsics.rs:54:14: 54:46 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Never {transmute::<(), Never>}, val: Value() } + _0 = move _2 as Never (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff new file mode 100644 index 0000000000000..9bb43d850ebf3 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff @@ -0,0 +1,60 @@ +- // MIR for `unchecked` before LowerIntrinsics ++ // MIR for `unchecked` after LowerIntrinsics + + fn unchecked(_1: i32, _2: i32) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:25: +0:26 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:33: +0:34 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:41: +0:41 + let _3: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let mut _4: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + let mut _5: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 + let mut _7: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + let mut _8: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:49: +2:50 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let _6: i32; // in scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 +- _3 = unchecked_div::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:16:14: 16:45 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(i32, i32) -> i32 {unchecked_div::}, val: Value() } ++ _3 = Div(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageLive(_6); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + StorageLive(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + _7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:49: +2:50 + _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:49: +2:50 +- _6 = unchecked_rem::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:17:14: 17:45 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(i32, i32) -> i32 {unchecked_rem::}, val: Value() } ++ _6 = Rem(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 ++ goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 + } + + bb2: { + StorageDead(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + StorageDead(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:41: +3:2 + StorageDead(_6); // scope 1 at $DIR/lower_intrinsics.rs:+3:1: +3:2 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+3:1: +3:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+3:2: +3:2 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff index 28e45909c3372..83c9c508bc0b0 100644 --- a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff @@ -13,7 +13,7 @@ StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - _2 = std::intrinsics::unreachable() -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:31:14: 31:43 +- // + span: $DIR/lower_intrinsics.rs:37:14: 37:43 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn() -> ! {std::intrinsics::unreachable}, val: Value() } + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 } diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff index 9cf4fbb88f389..4ae4466a60038 100644 --- a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff @@ -32,7 +32,7 @@ _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 - _3 = add_with_overflow::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:112:14: 112:49 +- // + span: $DIR/lower_intrinsics.rs:118:14: 118:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {add_with_overflow::}, val: Value() } + _3 = CheckedAdd(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 @@ -48,7 +48,7 @@ _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:53: +2:54 - _6 = sub_with_overflow::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:113:14: 113:49 +- // + span: $DIR/lower_intrinsics.rs:119:14: 119:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {sub_with_overflow::}, val: Value() } + _6 = CheckedSub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 + goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 @@ -64,7 +64,7 @@ _11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:+3:53: +3:54 - _9 = mul_with_overflow::(move _10, move _11) -> [return: bb3, unwind unreachable]; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:114:14: 114:49 +- // + span: $DIR/lower_intrinsics.rs:120:14: 120:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {mul_with_overflow::}, val: Value() } + _9 = CheckedMul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 + goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 diff --git a/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff index 38d99f661dc64..2eabd7f626b69 100644 --- a/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff @@ -17,7 +17,7 @@ _4 = move _2; // scope 1 at $DIR/lower_intrinsics.rs:+1:50: +1:51 - _0 = write_via_move::(move _3, move _4) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:129:14: 129:46 +- // + span: $DIR/lower_intrinsics.rs:135:14: 135:46 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*mut String, String) {write_via_move::}, val: Value() } + (*_3) = move _4; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 From bfb923326f8089b23cfd30fc1e963841ec83eb77 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Jun 2023 00:01:25 -0700 Subject: [PATCH 575/806] remove unchecked_div/_rem from cg_cranelift --- compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 0a513b08b74f5..1e83c30bd677a 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -475,9 +475,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul - | sym::unchecked_div | sym::exact_div - | sym::unchecked_rem | sym::unchecked_shl | sym::unchecked_shr => { intrinsic_args!(fx, args => (x, y); intrinsic); @@ -487,8 +485,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::unchecked_add => BinOp::Add, sym::unchecked_sub => BinOp::Sub, sym::unchecked_mul => BinOp::Mul, - sym::unchecked_div | sym::exact_div => BinOp::Div, - sym::unchecked_rem => BinOp::Rem, + sym::exact_div => BinOp::Div, sym::unchecked_shl => BinOp::Shl, sym::unchecked_shr => BinOp::Shr, _ => unreachable!(), From dc353f13b83f37ce5576788246acc7a109c17171 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Jun 2023 00:01:35 -0700 Subject: [PATCH 576/806] remove unchecked_div/_rem from cg_ssa --- compiler/rustc_codegen_ssa/src/mir/intrinsic.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 1479242f23a6c..9ac2424e76be0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -211,8 +211,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].val.unaligned_volatile_store(bx, dst); return; } - | sym::unchecked_div - | sym::unchecked_rem | sym::unchecked_shl | sym::unchecked_shr | sym::unchecked_add @@ -229,20 +227,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.exactudiv(args[0].immediate(), args[1].immediate()) } } - sym::unchecked_div => { - if signed { - bx.sdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.udiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_rem => { - if signed { - bx.srem(args[0].immediate(), args[1].immediate()) - } else { - bx.urem(args[0].immediate(), args[1].immediate()) - } - } sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()), sym::unchecked_shr => { if signed { From 73f104b6d6d5ae058a06a0f8eec3f370093e91a4 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Jun 2023 00:05:12 -0700 Subject: [PATCH 577/806] remove unchecked_div/_rem from ctfe --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a77c699c22f7e..a4b58c9849b83 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -233,9 +233,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::unchecked_shr | sym::unchecked_add | sym::unchecked_sub - | sym::unchecked_mul - | sym::unchecked_div - | sym::unchecked_rem => { + | sym::unchecked_mul => { let l = self.read_immediate(&args[0])?; let r = self.read_immediate(&args[1])?; let bin_op = match intrinsic_name { @@ -244,8 +242,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::unchecked_add => BinOp::Add, sym::unchecked_sub => BinOp::Sub, sym::unchecked_mul => BinOp::Mul, - sym::unchecked_div => BinOp::Div, - sym::unchecked_rem => BinOp::Rem, _ => bug!(), }; let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?; From a7d604d46f27996b4037bb57e7f472ee23d473b2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 09:37:55 +0200 Subject: [PATCH 578/806] fix: Fix nav target calculation discarding file ids from differing macro upmapping --- crates/ide/src/navigation_target.rs | 108 ++++++++++++++-------------- crates/ide/src/references.rs | 4 +- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 7ec7788a245b0..385c1b0c00816 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -5,7 +5,7 @@ use std::fmt; use either::Either; use hir::{ symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, - HirDisplay, InFile, LocalSource, ModuleSource, + HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, }; use ide_db::{ base_db::{FileId, FileRange}, @@ -92,10 +92,9 @@ impl NavigationTarget { pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); - if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) { - let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db); - let focus_range = - value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax())); + if let Some(InFile { value, file_id }) = &module.declaration_source(db) { + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, *file_id, value.syntax(), value.name()); let mut res = NavigationTarget::from_syntax( file_id, name, @@ -131,14 +130,15 @@ impl NavigationTarget { /// Allows `NavigationTarget` to be created from a `NameOwner` pub(crate) fn from_named( db: &RootDatabase, - node @ InFile { file_id, value }: InFile<&dyn ast::HasName>, + InFile { file_id, value }: InFile<&dyn ast::HasName>, kind: SymbolKind, ) -> NavigationTarget { let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db); - NavigationTarget::from_syntax(file_id, name, focus_range, range, kind) + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.name()); + + NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind) } fn from_syntax( @@ -165,7 +165,13 @@ impl NavigationTarget { impl TryToNav for FileSymbol { fn try_to_nav(&self, db: &RootDatabase) -> Option { let full_range = self.loc.original_range(db); - let name_range = self.loc.original_name_range(db)?; + let focus_range = self.loc.original_name_range(db).and_then(|it| { + if it.file_id == full_range.file_id { + Some(it.range) + } else { + None + } + }); Some(NavigationTarget { file_id: full_range.file_id, @@ -173,7 +179,7 @@ impl TryToNav for FileSymbol { alias: if self.is_alias { Some(self.name.clone()) } else { None }, kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, - focus_range: Some(name_range.range), + focus_range, container_name: self.container_name.clone(), description: match self.def { hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), @@ -340,15 +346,11 @@ impl ToNav for hir::Module { let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); let (syntax, focus) = match &value { ModuleSource::SourceFile(node) => (node.syntax(), None), - ModuleSource::Module(node) => ( - node.syntax(), - node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())), - ), + ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; - let FileRange { file_id, range: full_range } = - InFile::new(file_id, syntax).original_file_range(db); - NavigationTarget::from_syntax(file_id, name, focus, full_range, SymbolKind::Module) + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); + NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module) } } @@ -357,17 +359,14 @@ impl TryToNav for hir::Impl { let InFile { file_id, value } = self.source(db)?; let derive_attr = self.is_builtin_derive(db); - let focus_range = if derive_attr.is_some() { - None - } else { - value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax())) - }; + let focus = if derive_attr.is_some() { None } else { value.self_ty() }; - let FileRange { file_id, range: full_range } = match &derive_attr { - Some(attr) => attr.syntax().original_file_range(db), - None => InFile::new(file_id, value.syntax()).original_file_range(db), + let syntax = match &derive_attr { + Some(attr) => attr.value.syntax(), + None => value.syntax(), }; + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); Some(NavigationTarget::from_syntax( file_id, "impl".into(), @@ -456,9 +455,8 @@ impl ToNav for LocalSource { Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()), Either::Right(it) => (it.syntax(), it.name()), }; - let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range: full_range } = - InFile::new(file_id, node).original_file_range(db); + + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name); let name = local.name(db).to_smol_str(); let kind = if local.is_self(db) { @@ -493,9 +491,8 @@ impl ToNav for hir::Label { let InFile { file_id, value } = self.source(db); let name = self.name(db).to_smol_str(); - let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db); - let FileRange { file_id, range: full_range } = range(value.syntax()); - let focus_range = value.lifetime().map(|lt| range(lt.syntax()).range); + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()); NavigationTarget { file_id, @@ -525,19 +522,14 @@ impl TryToNav for hir::TypeParam { Either::Right(x) => Either::Right(x), }; - let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db); - let focus_range = |syntax: &_| InFile::new(file_id, syntax).original_file_range_opt(db); - let FileRange { file_id, range: full_range } = match &value { - Either::Left(type_param) => range(type_param.syntax()), - Either::Right(trait_) => trait_ - .name() - .and_then(|name| focus_range(name.syntax())) - .unwrap_or_else(|| range(trait_.syntax())), + let syntax = match &value { + Either::Left(type_param) => type_param.syntax(), + Either::Right(trait_) => trait_.syntax(), }; - let focus_range = value - .either(|it| it.name(), |it| it.name()) - .and_then(|it| focus_range(it.syntax())) - .map(|it| it.range); + let focus = value.as_ref().either(|it| it.name(), |it| it.name()); + + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); + Some(NavigationTarget { file_id, name, @@ -563,15 +555,15 @@ impl TryToNav for hir::LifetimeParam { let InFile { file_id, value } = self.source(db)?; let name = self.name(db).to_smol_str(); - let FileRange { file_id, range: full_range } = + let FileRange { file_id, range } = InFile::new(file_id, value.syntax()).original_file_range(db); Some(NavigationTarget { file_id, name, alias: None, kind: Some(SymbolKind::LifetimeParam), - full_range, - focus_range: Some(full_range), + full_range: range, + focus_range: Some(range), container_name: None, description: None, docs: None, @@ -592,9 +584,8 @@ impl TryToNav for hir::ConstParam { } }; - let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range: full_range } = - InFile::new(file_id, value.syntax()).original_file_range(db); + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.name()); Some(NavigationTarget { file_id, name, @@ -609,12 +600,19 @@ impl TryToNav for hir::ConstParam { } } -fn orig_focus_range( +fn orig_range_with_focus( db: &RootDatabase, - file_id: hir::HirFileId, - syntax: &SyntaxNode, -) -> Option { - InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range) + hir_file: HirFileId, + value: &SyntaxNode, + name: Option, +) -> (FileId, TextRange, Option) { + let FileRange { file_id, range: full_range } = + InFile::new(hir_file, value).original_file_range(db); + let focus_range = name + .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db)) + .and_then(|range| if range.file_id == file_id { Some(range.range) } else { None }); + + (file_id, full_range, focus_range) } #[cfg(test)] diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 291b1a349b347..fdc5261ac38b2 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -1289,7 +1289,7 @@ trait Foo where Self$0 { impl Foo for () {} "#, expect![[r#" - Self TypeParam FileId(0) 6..9 6..9 + Self TypeParam FileId(0) 0..44 6..9 FileId(0) 16..20 FileId(0) 37..41 @@ -1380,7 +1380,7 @@ fn foo(_: impl Bar, _: &dyn Bar) {} trait Foo = where Self$0: ; "#, expect![[r#" - Self TypeParam FileId(0) 6..9 6..9 + Self TypeParam FileId(0) 0..25 6..9 FileId(0) 18..22 "#]], From 5f5e2e2ac137b19f2f2a21562ca34a6edb1a7d8f Mon Sep 17 00:00:00 2001 From: MarcusGrass Date: Thu, 1 Jun 2023 12:14:55 +0200 Subject: [PATCH 579/806] Explain which paths clippy searches for configuration in docs --- book/src/configuration.md | 6 ++++-- book/src/development/adding_lints.md | 6 ++++-- clippy_lints/src/utils/conf.rs | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index 1304f6a8c2f09..54af30d17e1cd 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -2,8 +2,10 @@ > **Note:** The configuration file is unstable and may be deprecated in the future. -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a -basic `variable = value` mapping e.g. +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, placed in a directory specified by +the environment variable `CLIPPY_CONF_DIR`, or if that's not found, the environment variable +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html), or if that isn't +found, the current directory. It contains a basic `variable = value` mapping e.g. ```toml avoid-breaking-exported-api = false diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index ccae8d3742018..ea3819ccbdf2c 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -630,8 +630,10 @@ Before submitting your PR make sure you followed all the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in -the workspace directory. Adding a configuration to a lint can be useful for +Clippy supports the configuration of lints values using a `clippy.toml` file in a directory specified by +the environment variable `CLIPPY_CONF_DIR`, or if that's not found, the environment variable +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html), or if that isn't +found, the current directory. Adding a configuration to a lint can be useful for thresholds or to constrain some behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f6de66bb5145b..4acff7fd32287 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -486,7 +486,7 @@ pub fn lookup_conf_file() -> io::Result<(Option, Vec)> { const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR. - // If neither of those exist, use ".". + // If neither of those exist, use ".". (Update documentation if this priority changes) let mut current = env::var_os("CLIPPY_CONF_DIR") .or_else(|| env::var_os("CARGO_MANIFEST_DIR")) .map_or_else(|| PathBuf::from("."), PathBuf::from) From 194343fceaa30c021af3fb4456acd16aa8034797 Mon Sep 17 00:00:00 2001 From: MarcusGrass Date: Thu, 1 Jun 2023 12:54:20 +0200 Subject: [PATCH 580/806] Explain path-search using a list --- book/src/configuration.md | 12 ++++++++---- book/src/development/adding_lints.md | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index 54af30d17e1cd..e8274bc4575d0 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -2,10 +2,14 @@ > **Note:** The configuration file is unstable and may be deprecated in the future. -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, placed in a directory specified by -the environment variable `CLIPPY_CONF_DIR`, or if that's not found, the environment variable -[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html), or if that isn't -found, the current directory. It contains a basic `variable = value` mapping e.g. +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in: + +1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or +2. The directory specified by the +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or +3. The current directory. + +It contains a basic `variable = value` mapping e.g. ```toml avoid-breaking-exported-api = false diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index ea3819ccbdf2c..c26aa883ebae2 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -630,10 +630,14 @@ Before submitting your PR make sure you followed all the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in a directory specified by -the environment variable `CLIPPY_CONF_DIR`, or if that's not found, the environment variable -[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html), or if that isn't -found, the current directory. Adding a configuration to a lint can be useful for +Clippy supports the configuration of lints values using a `clippy.toml` file which is searched for in: + +1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or +2. The directory specified by the +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or +3. The current directory. + +Adding a configuration to a lint can be useful for thresholds or to constrain some behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: From 793fa8d7c5535867510de90b945f1e01d0eca496 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 1 Jun 2023 12:55:58 +0200 Subject: [PATCH 581/806] doc: improve explanation --- library/std/src/sync/once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 1b17c31089ff2..8c46080e43b38 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -91,7 +91,7 @@ impl Once { /// return). /// /// If the given closure recursively invokes `call_once` on the same [`Once`] - /// instance the exact behavior is not specified, allowed outcomes are + /// instance, the exact behavior is not specified: allowed outcomes are /// a panic or a deadlock. /// /// # Examples From dc7c6d43c77c2edb549f574188da8aff4ac0fba4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 14:46:36 +0200 Subject: [PATCH 582/806] Slightly shrink `DefMap` --- crates/hir-def/src/body/lower.rs | 2 +- crates/hir-def/src/child_by_source.rs | 3 +- crates/hir-def/src/db.rs | 11 +++- crates/hir-def/src/import_map.rs | 6 +- .../hir-def/src/macro_expansion_tests/mod.rs | 4 +- crates/hir-def/src/nameres.rs | 57 +++++++++++++------ crates/hir-def/src/nameres/collector.rs | 35 +++++------- crates/hir-def/src/nameres/path_resolution.rs | 29 +++++----- crates/hir-def/src/nameres/tests/macros.rs | 10 ++-- crates/hir-def/src/resolver.rs | 8 +-- crates/hir-def/src/test_db.rs | 4 +- crates/hir-ty/src/display.rs | 3 +- crates/hir/src/display.rs | 2 +- crates/hir/src/lib.rs | 11 ++-- crates/ide-completion/src/completions.rs | 2 +- crates/ide-db/src/helpers.rs | 2 +- crates/ide-db/src/rename.rs | 2 +- crates/ide-db/src/search.rs | 6 +- .../src/handlers/unlinked_file.rs | 4 +- crates/ide/src/status.rs | 3 +- .../ide/src/syntax_highlighting/highlight.rs | 2 +- lib/la-arena/src/lib.rs | 16 +++++- 22 files changed, 130 insertions(+), 92 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index ebe05afca6a81..7055e3ca9e2b8 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1073,7 +1073,7 @@ impl ExprCollector<'_> { match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { Some((def_map, block_id)) => { self.body.block_scopes.push(block_id); - (def_map.module_id(def_map.root()), def_map) + (def_map.module_id(DefMap::ROOT), def_map) } None => (self.expander.module, self.def_map.clone()), }; diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs index 21180fcbdae40..bb79e28f2673a 100644 --- a/crates/hir-def/src/child_by_source.rs +++ b/crates/hir-def/src/child_by_source.rs @@ -12,6 +12,7 @@ use crate::{ db::DefDatabase, dyn_map::{keys, DynMap}, item_scope::ItemScope, + nameres::DefMap, src::{HasChildSource, HasSource}, AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, VariantId, @@ -205,7 +206,7 @@ impl ChildBySource for DefWithBodyId { for (_, def_map) in body.blocks(db) { // All block expressions are merged into the same map, because they logically all add // inner items to the containing `DefWithBodyId`. - def_map[def_map.root()].scope.child_by_source_to(db, res, file_id); + def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id); } } } diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index c3721a94f6743..6d18e3f56cabc 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -198,15 +198,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; - #[salsa::invoke(LangItems::lang_item_query)] fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option; #[salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: CrateId) -> Arc; + // region:visibilities + #[salsa::invoke(visibility::field_visibilities_query)] fn field_visibilities(&self, var: VariantId) -> Arc>; @@ -217,8 +216,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Visibility; + // endregion:visibilities + + #[salsa::invoke(LangItems::crate_lang_items_query)] + fn crate_lang_items(&self, krate: CrateId) -> Arc; + #[salsa::transparent] fn crate_limits(&self, crate_id: CrateId) -> CrateLimits; + #[salsa::transparent] fn recursion_limit(&self, crate_id: CrateId) -> u32; diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 6ef2949ef51a1..ec150dc0689c2 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -11,8 +11,8 @@ use rustc_hash::{FxHashSet, FxHasher}; use triomphe::Arc; use crate::{ - db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, - ModuleId, TraitId, + db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId, + ModuleDefId, ModuleId, TraitId, }; type FxIndexMap = IndexMap>; @@ -183,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { // We look only into modules that are public(ly reexported), starting with the crate root. let empty = ImportPath { segments: vec![] }; - let root = def_map.module_id(def_map.root()); + let root = def_map.module_id(DefMap::ROOT); let mut worklist = vec![(root, empty)]; while let Some((module, mod_path)) = worklist.pop() { let ext_def_map; diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 40849d4a66d8c..4a62696df0810 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -35,7 +35,7 @@ use tt::token_id::{Subtree, TokenId}; use crate::{ db::DefDatabase, macro_id_to_def_id, - nameres::{MacroSubNs, ModuleSource}, + nameres::{DefMap, MacroSubNs, ModuleSource}, resolver::HasResolver, src::HasSource, test_db::TestDB, @@ -61,7 +61,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros); let krate = db.crate_graph().iter().next().unwrap(); let def_map = db.crate_def_map(krate); - let local_id = def_map.root(); + let local_id = DefMap::ROOT; let module = def_map.module_id(local_id); let resolver = module.resolver(&db); let source = def_map[local_id].definition_source(&db); diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index ccb9bed5c5017..053ab5890e4f6 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -94,7 +94,6 @@ use crate::{ pub struct DefMap { _c: Count, block: Option, - root: LocalModuleId, modules: Arena, krate: CrateId, /// The prelude module for this crate. This either comes from an import @@ -141,7 +140,19 @@ struct BlockInfo { /// The `BlockId` this `DefMap` was created from. block: BlockId, /// The containing module. - parent: ModuleId, + parent: BlockRelativeModuleId, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +struct BlockRelativeModuleId { + block: Option, + local_id: LocalModuleId, +} + +impl BlockRelativeModuleId { + fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc { + ModuleId { krate, block: self.block, local_id: self.local_id }.def_map(db) + } } impl std::ops::Index for DefMap { @@ -231,6 +242,8 @@ pub struct ModuleData { } impl DefMap { + pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0)); + pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("crate_def_map_query").detail(|| { db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() @@ -266,7 +279,13 @@ impl DefMap { ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); let mut def_map = DefMap::empty(krate, parent_map.edition, module_data); - def_map.block = Some(BlockInfo { block: block_id, parent: block.module }); + def_map.block = Some(BlockInfo { + block: block_id, + parent: BlockRelativeModuleId { + block: block.module.block, + local_id: block.module.local_id, + }, + }); let def_map = collector::collect_defs(db, def_map, tree_id); Arc::new(def_map) @@ -275,6 +294,7 @@ impl DefMap { fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap { let mut modules: Arena = Arena::default(); let root = modules.alloc(module_data); + assert_eq!(root, Self::ROOT); DefMap { _c: Count::new(), @@ -289,7 +309,6 @@ impl DefMap { proc_macro_loading_error: None, derive_helpers_in_scope: FxHashMap::default(), prelude: None, - root, modules, registered_attrs: Vec::new(), registered_tools: Vec::new(), @@ -339,10 +358,6 @@ impl DefMap { self.no_std || self.no_core } - pub fn root(&self) -> LocalModuleId { - self.root - } - pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.fn_proc_macro_mapping.get(&id).copied() } @@ -377,9 +392,9 @@ impl DefMap { } pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { - self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { + self.with_ancestor_maps(db, Self::ROOT, &mut |def_map, _module| { if def_map.block.is_none() { - Some(def_map.module_id(def_map.root)) + Some(def_map.module_id(Self::ROOT)) } else { None } @@ -439,7 +454,7 @@ impl DefMap { } let mut block = self.block; while let Some(block_info) = block { - let parent = block_info.parent.def_map(db); + let parent = block_info.parent.def_map(db, self.krate); if let Some(it) = f(&parent, block_info.parent.local_id) { return Some(it); } @@ -452,7 +467,8 @@ impl DefMap { /// If this `DefMap` is for a block expression, returns the module containing the block (which /// might again be a block, or a module inside a block). pub fn parent(&self) -> Option { - Some(self.block?.parent) + let BlockRelativeModuleId { block, local_id } = self.block?.parent; + Some(ModuleId { krate: self.krate, block, local_id }) } /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing @@ -460,7 +476,13 @@ impl DefMap { pub fn containing_module(&self, local_mod: LocalModuleId) -> Option { match self[local_mod].parent { Some(parent) => Some(self.module_id(parent)), - None => self.block.map(|block| block.parent), + None => { + self.block.map( + |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| { + ModuleId { krate: self.krate, block, local_id } + }, + ) + } } } @@ -471,12 +493,12 @@ impl DefMap { let mut arc; let mut current_map = self; while let Some(block) = current_map.block { - go(&mut buf, db, current_map, "block scope", current_map.root); + go(&mut buf, db, current_map, "block scope", Self::ROOT); buf.push('\n'); - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, self.krate); current_map = &arc; } - go(&mut buf, db, current_map, "crate", current_map.root); + go(&mut buf, db, current_map, "crate", Self::ROOT); return buf; fn go( @@ -506,7 +528,7 @@ impl DefMap { let mut current_map = self; while let Some(block) = current_map.block { format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, self.krate); current_map = &arc; } @@ -534,7 +556,6 @@ impl DefMap { recursion_limit: _, krate: _, prelude: _, - root: _, rustc_coherence_is_core: _, no_core: _, no_std: _, diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 64caf26299ca0..b431b6f64739c 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -71,7 +71,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T for dep in &krate.dependencies { tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); let dep_def_map = db.crate_def_map(dep.crate_id); - let dep_root = dep_def_map.module_id(dep_def_map.root); + let dep_root = dep_def_map.module_id(DefMap::ROOT); deps.insert(dep.as_name(), dep_root); @@ -287,7 +287,7 @@ impl DefCollector<'_> { let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; let item_tree = self.db.file_item_tree(file_id.into()); - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); @@ -382,7 +382,7 @@ impl DefCollector<'_> { fn seed_with_inner(&mut self, tree_id: TreeId) { let item_tree = tree_id.item_tree(self.db); - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; let is_cfg_enabled = item_tree .top_level_attrs(self.db, self.def_map.krate) @@ -464,7 +464,7 @@ impl DefCollector<'_> { // Additionally, while the proc macro entry points must be `pub`, they are not publicly // exported in type/value namespace. This function reduces the visibility of all items // in the crate root that aren't proc macros. - let root = self.def_map.root; + let root = DefMap::ROOT; let module_id = self.def_map.module_id(root); let root = &mut self.def_map.modules[root]; root.scope.censor_non_proc_macros(module_id); @@ -560,13 +560,8 @@ impl DefCollector<'_> { }; let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]); - let (per_ns, _) = self.def_map.resolve_path( - self.db, - self.def_map.root, - &path, - BuiltinShadowMode::Other, - None, - ); + let (per_ns, _) = + self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); match per_ns.types { Some((ModuleDefId::ModuleId(m), _)) => { @@ -661,7 +656,7 @@ impl DefCollector<'_> { // In Rust, `#[macro_export]` macros are unconditionally visible at the // crate root, even if the parent modules is **not** visible. if export { - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, @@ -712,7 +707,7 @@ impl DefCollector<'_> { /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. /// And unconditionally exported. fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) { - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, @@ -732,7 +727,7 @@ impl DefCollector<'_> { let def_map = self.db.crate_def_map(krate); // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` // macros. - let root_scope = &def_map[def_map.root].scope; + let root_scope = &def_map[DefMap::ROOT].scope; if let Some(names) = names { for name in names { // FIXME: Report diagnostic on 404. @@ -834,9 +829,9 @@ impl DefCollector<'_> { let root = match self.def_map.block { Some(_) => { let def_map = self.def_map.crate_root(self.db).def_map(self.db); - def_map.module_id(def_map.root()) + def_map.module_id(DefMap::ROOT) } - None => self.def_map.module_id(self.def_map.root()), + None => self.def_map.module_id(DefMap::ROOT), }; Some(root) } else { @@ -879,7 +874,7 @@ impl DefCollector<'_> { // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 if import.is_extern_crate && self.def_map.block.is_none() - && module_id == self.def_map.root + && module_id == DefMap::ROOT { if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name) { @@ -1525,7 +1520,7 @@ impl ModCollector<'_, '_> { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { let krate = self.def_collector.def_map.krate; - let is_crate_root = self.module_id == self.def_collector.def_map.root; + let is_crate_root = self.module_id == DefMap::ROOT; // Note: don't assert that inserted value is fresh: it's simply not true // for macros. @@ -1641,9 +1636,9 @@ impl ModCollector<'_, '_> { FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db); let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); - if self.def_collector.is_proc_macro && self.module_id == def_map.root { + if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT { if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { - let crate_root = def_map.module_id(def_map.root); + let crate_root = def_map.module_id(DefMap::ROOT); self.def_collector.export_proc_macro( proc_macro, ItemTreeId::new(self.tree_id, id), diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 24dc4e243bf4a..751536db3848f 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -121,7 +121,7 @@ impl DefMap { // ...unless we're resolving visibility for an associated item in an impl. if self.block_id() != m.block && !within_impl { cov_mark::hit!(adjust_vis_in_block_def_map); - vis = Visibility::Module(self.module_id(self.root())); + vis = Visibility::Module(self.module_id(Self::ROOT)); tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); } } @@ -173,7 +173,7 @@ impl DefMap { match ¤t_map.block { Some(block) => { original_module = block.parent.local_id; - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, current_map.krate); current_map = &*arc; } None => return result, @@ -207,7 +207,7 @@ impl DefMap { PerNs::types(self.crate_root(db).into(), Visibility::Public) } else { let def_map = db.crate_def_map(krate); - let module = def_map.module_id(def_map.root); + let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); PerNs::types(module.into(), Visibility::Public) } @@ -268,14 +268,17 @@ impl DefMap { path.display(db.upcast()), new_path.display(db.upcast()) ); - return block.parent.def_map(db).resolve_path_fp_with_macro( - db, - mode, - block.parent.local_id, - &new_path, - shadow, - expected_macro_subns, - ); + return block + .parent + .def_map(db, self.krate) + .resolve_path_fp_with_macro( + db, + mode, + block.parent.local_id, + &new_path, + shadow, + expected_macro_subns, + ); } None => { tracing::debug!("super path in root module"); @@ -476,9 +479,9 @@ impl DefMap { let from_crate_root = match self.block { Some(_) => { let def_map = self.crate_root(db).def_map(db); - def_map[def_map.root].scope.get(name) + def_map[Self::ROOT].scope.get(name) } - None => self[self.root].scope.get(name), + None => self[Self::ROOT].scope.get(name), }; let from_extern_prelude = || { self.resolve_name_in_extern_prelude(db, name) diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 57f0233607b31..ae509de05633e 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -750,7 +750,7 @@ macro_rules! foo { pub use core::clone::Clone; "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 1); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] @@ -772,7 +772,7 @@ pub macro Copy {} pub macro Clone {} "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 2); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2); } #[test] @@ -815,7 +815,7 @@ pub macro derive($item:item) {} pub macro Clone {} "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 1); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] @@ -1286,7 +1286,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } let krate = db.crate_graph().iter().next().unwrap(); let def_map = db.crate_def_map(krate); - let root_module = &def_map[def_map.root()].scope; + let root_module = &def_map[DefMap::ROOT].scope; assert!( root_module.legacy_macros().count() == 0, "`#[macro_use]` shouldn't bring macros into textual macro scope", @@ -1392,7 +1392,7 @@ macro_rules! derive { () => {} } struct S; "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 1); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 3eaff61b154a0..06f5b2526a459 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -586,8 +586,9 @@ impl Resolver { })); if let Some(block) = expr_scopes.block(scope_id) { let def_map = db.block_def_map(block); - let root = def_map.root(); - resolver.scopes.push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root })); + resolver + .scopes + .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT })); // FIXME: This adds as many module scopes as there are blocks, but resolving in each // already traverses all parents, so this is O(n²). I think we could only store the // innermost module scope instead? @@ -753,8 +754,7 @@ fn resolver_for_scope_( for scope in scope_chain.into_iter().rev() { if let Some(block) = scopes.block(scope) { let def_map = db.block_def_map(block); - let root = def_map.root(); - r = r.push_block_scope(def_map, root); + r = r.push_block_scope(def_map, DefMap::ROOT); // FIXME: This adds as many module scopes as there are blocks, but resolving in each // already traverses all parents, so this is O(n²). I think we could only store the // innermost module scope instead? diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index d4b403136429d..a6befc8a81a81 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -110,7 +110,7 @@ impl TestDB { } _ => { // FIXME: handle `mod` inside block expression - return def_map.module_id(def_map.root()); + return def_map.module_id(DefMap::ROOT); } } } @@ -119,7 +119,7 @@ impl TestDB { /// Finds the smallest/innermost module in `def_map` containing `position`. fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId { let mut size = None; - let mut res = def_map.root(); + let mut res = DefMap::ROOT; for (module, data) in def_map.modules() { let src = data.definition_source(self); if src.file_id != position.file_id.into() { diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 058d5059b1692..32f50cb051dbc 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -13,6 +13,7 @@ use hir_def::{ generics::{TypeOrConstParamData, TypeParamProvenance}, item_scope::ItemInNs, lang_item::{LangItem, LangItemTarget}, + nameres::DefMap, path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, @@ -1488,7 +1489,7 @@ pub fn write_visibility( Visibility::Public => write!(f, "pub "), Visibility::Module(vis_id) => { let def_map = module_id.def_map(f.db.upcast()); - let root_module_id = def_map.module_id(def_map.root()); + let root_module_id = def_map.module_id(DefMap::ROOT); if vis_id == module_id { // pub(self) or omitted Ok(()) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 0c8793c6df031..9a2090ab79a2c 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -548,7 +548,7 @@ impl HirDisplay for Module { // FIXME: Module doesn't have visibility saved in data. match self.name(f.db) { Some(name) => write!(f, "mod {}", name.display(f.db.upcast())), - None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) { + None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) { Some(name) => write!(f, "extern crate {name}"), None => f.write_str("extern crate {unknown}"), }, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7c432197a60a7..19709bb44ad45 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -118,7 +118,7 @@ pub use { find_path::PrefixKind, import_map, lang_item::LangItem, - nameres::ModuleSource, + nameres::{DefMap, ModuleSource}, path::{ModPath, PathKind}, type_ref::{Mutability, TypeRef}, visibility::Visibility, @@ -202,7 +202,7 @@ impl Crate { pub fn root_module(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } pub fn modules(self, db: &dyn HirDatabase) -> Vec { @@ -475,12 +475,11 @@ impl Module { /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id.krate()); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } - pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool { - let def_map = db.crate_def_map(self.id.krate()); - def_map.root() == self.id.local_id + pub fn is_crate_root(self) -> bool { + DefMap::ROOT == self.id.local_id } /// Iterates over all child modules. diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index d53e9e2fa9078..480cb77b4fd6a 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -158,7 +158,7 @@ impl Completions { path_ctx: &PathCompletionCtx, ) { ctx.process_all_names(&mut |name, res, doc_aliases| match res { - ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { + ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => { self.add_module(ctx, path_ctx, m, name, doc_aliases); } _ => (), diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 8e3b1eef15b2d..eba9d8afc40ab 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -77,7 +77,7 @@ pub fn visit_file_defs( } module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); - let is_root = module.is_crate_root(db); + let is_root = module.is_crate_root(); module .legacy_macros(db) .into_iter() diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 5b2558ea8fe03..52a23b4b8f3d2 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -178,7 +178,7 @@ fn rename_mod( let mut source_change = SourceChange::default(); - if module.is_crate_root(sema.db) { + if module.is_crate_root() { return Ok(source_change); } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 9d00c7170975d..73cd5dcaf23fc 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -225,7 +225,7 @@ impl Definition { // def is crate root // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name if let &Definition::Module(module) = self { - if module.is_crate_root(db) { + if module.is_crate_root() { return SearchScope::reverse_dependencies(db, module.krate()); } } @@ -392,7 +392,7 @@ impl<'a> FindUsages<'a> { let name = match self.def { // special case crate modules as these do not have a proper name - Definition::Module(module) if module.is_crate_root(self.sema.db) => { + Definition::Module(module) if module.is_crate_root() => { // FIXME: This assumes the crate name is always equal to its display name when it really isn't module .krate() @@ -500,7 +500,7 @@ impl<'a> FindUsages<'a> { let scope = search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module)); - let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); + let is_crate_root = module.is_crate_root().then(|| Finder::new("crate")); let finder = &Finder::new("super"); for (text, file_id, search_range) in scope_files(sema, &scope) { diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index b9c5384ea1eec..271e7ce73bcdd 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -2,7 +2,7 @@ use std::iter; -use hir::{db::DefDatabase, InFile, ModuleSource}; +use hir::{db::DefDatabase, DefMap, InFile, ModuleSource}; use ide_db::{ base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, source_change::SourceChange, @@ -74,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { 'crates: for &krate in &*ctx.sema.db.relevant_crates(file_id) { let crate_def_map = ctx.sema.db.crate_def_map(krate); - let root_module = &crate_def_map[crate_def_map.root()]; + let root_module = &crate_def_map[DefMap::ROOT]; let Some(root_file_id) = root_module.origin.file_id() else { continue }; let Some(crate_root_path) = source_root.path_for_file(&root_file_id) else { continue }; let Some(rel) = parent.strip_prefix(&crate_root_path.parent()?) else { continue }; diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 70bff41121d81..d2c77e2dc7975 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -1,7 +1,7 @@ use std::{fmt, marker::PhantomData}; use hir::{ - db::{AstIdMapQuery, AttrsQuery, ParseMacroExpansionQuery}, + db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery}, Attr, Attrs, ExpandResult, MacroFile, Module, }; use ide_db::{ @@ -51,6 +51,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { format_to!(buf, "\nDebug info:\n"); format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db))); format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db))); + format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db))); if let Some(file_id) = file_id { format_to!(buf, "\nFile info:\n"); diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 925057ffaa090..3c40246a69d37 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -340,7 +340,7 @@ fn highlight_def( Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)), Definition::Module(module) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); - if module.is_crate_root(db) { + if module.is_crate_root() { h |= HlMod::CrateRoot; } h diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs index 1f8ef01a5bcea..5107f294394bc 100644 --- a/lib/la-arena/src/lib.rs +++ b/lib/la-arena/src/lib.rs @@ -18,6 +18,18 @@ pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RawIdx(u32); +impl RawIdx { + /// Constructs a [`RawIdx`] from a u32. + pub const fn from_u32(u32: u32) -> Self { + RawIdx(u32) + } + + /// Deconstructs a [`RawIdx`] into the underlying u32. + pub const fn into_u32(self) -> u32 { + self.0 + } +} + impl From for u32 { #[inline] fn from(raw: RawIdx) -> u32 { @@ -94,12 +106,12 @@ impl fmt::Debug for Idx { impl Idx { /// Creates a new index from a [`RawIdx`]. - pub fn from_raw(raw: RawIdx) -> Self { + pub const fn from_raw(raw: RawIdx) -> Self { Idx { raw, _ty: PhantomData } } /// Converts this index into the underlying [`RawIdx`]. - pub fn into_raw(self) -> RawIdx { + pub const fn into_raw(self) -> RawIdx { self.raw } } From 5c466ccc2bdfc978abccc988b59018273b5a210e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 15:04:38 +0200 Subject: [PATCH 583/806] Refactor out some crate wide data in DefMap into nested struct --- crates/hir-def/src/body/tests/block.rs | 4 +- crates/hir-def/src/nameres.rs | 83 +++++++++++-------- crates/hir-def/src/nameres/attr_resolution.rs | 4 +- crates/hir-def/src/nameres/collector.rs | 34 ++++---- crates/hir-def/src/nameres/path_resolution.rs | 2 +- crates/hir-def/src/nameres/tests/macros.rs | 4 +- 6 files changed, 73 insertions(+), 58 deletions(-) diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs index 2899ca4337d20..6e77744f21539 100644 --- a/crates/hir-def/src/body/tests/block.rs +++ b/crates/hir-def/src/body/tests/block.rs @@ -148,8 +148,8 @@ fn f() { } "#, expect![[r#" - BlockId(1) in ModuleId { krate: Idx::(0), block: Some(BlockId(0)), local_id: Idx::(1) } - BlockId(0) in ModuleId { krate: Idx::(0), block: None, local_id: Idx::(0) } + BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::(1) } + BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } crate scope "#]], ); diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 053ab5890e4f6..86ba9052031c6 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -109,14 +109,23 @@ pub struct DefMap { /// this contains all kinds of macro, not just `macro_rules!` macro. macro_use_prelude: FxHashMap, + /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper + /// attributes. + derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, + + diagnostics: Vec, + + // FIXME: Arc this so we can share it with block def maps + data: CrateData, +} + +#[derive(Debug, PartialEq, Eq)] +struct CrateData { /// Side table for resolving derive helpers. exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, /// The error that occurred when failing to load the proc-macro dll. proc_macro_loading_error: Option>, - /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper - /// attributes. - derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, /// Custom attributes registered with `#![register_attr]`. registered_attrs: Vec, @@ -131,7 +140,6 @@ pub struct DefMap { edition: Edition, recursion_limit: Option, - diagnostics: Vec, } /// For `DefMap`s computed for a block expression, this stores its location in the parent map. @@ -278,7 +286,7 @@ impl DefMap { let module_data = ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); - let mut def_map = DefMap::empty(krate, parent_map.edition, module_data); + let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data); def_map.block = Some(BlockInfo { block: block_id, parent: BlockRelativeModuleId { @@ -300,23 +308,25 @@ impl DefMap { _c: Count::new(), block: None, krate, - edition, - recursion_limit: None, extern_prelude: FxHashMap::default(), macro_use_prelude: FxHashMap::default(), - exported_derives: FxHashMap::default(), - fn_proc_macro_mapping: FxHashMap::default(), - proc_macro_loading_error: None, derive_helpers_in_scope: FxHashMap::default(), prelude: None, modules, - registered_attrs: Vec::new(), - registered_tools: Vec::new(), - unstable_features: FxHashSet::default(), diagnostics: Vec::new(), - rustc_coherence_is_core: false, - no_core: false, - no_std: false, + data: CrateData { + recursion_limit: None, + exported_derives: FxHashMap::default(), + fn_proc_macro_mapping: FxHashMap::default(), + proc_macro_loading_error: None, + registered_attrs: Vec::new(), + registered_tools: Vec::new(), + unstable_features: FxHashSet::default(), + rustc_coherence_is_core: false, + no_core: false, + no_std: false, + edition, + }, } } @@ -339,31 +349,31 @@ impl DefMap { } pub fn registered_tools(&self) -> &[SmolStr] { - &self.registered_tools + &self.data.registered_tools } pub fn registered_attrs(&self) -> &[SmolStr] { - &self.registered_attrs + &self.data.registered_attrs } pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool { - self.unstable_features.contains(feature) + self.data.unstable_features.contains(feature) } pub fn is_rustc_coherence_is_core(&self) -> bool { - self.rustc_coherence_is_core + self.data.rustc_coherence_is_core } pub fn is_no_std(&self) -> bool { - self.no_std || self.no_core + self.data.no_std || self.data.no_core } pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { - self.fn_proc_macro_mapping.get(&id).copied() + self.data.fn_proc_macro_mapping.get(&id).copied() } pub fn proc_macro_loading_error(&self) -> Option<&str> { - self.proc_macro_loading_error.as_deref() + self.data.proc_macro_loading_error.as_deref() } pub fn krate(&self) -> CrateId { @@ -540,25 +550,28 @@ impl DefMap { // Exhaustive match to require handling new fields. let Self { _c: _, - exported_derives, extern_prelude, macro_use_prelude, diagnostics, modules, - registered_attrs, - registered_tools, - fn_proc_macro_mapping, derive_helpers_in_scope, - unstable_features, - proc_macro_loading_error: _, block: _, - edition: _, - recursion_limit: _, krate: _, prelude: _, - rustc_coherence_is_core: _, - no_core: _, - no_std: _, + data: + CrateData { + exported_derives, + fn_proc_macro_mapping, + registered_attrs, + registered_tools, + unstable_features, + proc_macro_loading_error: _, + rustc_coherence_is_core: _, + no_core: _, + no_std: _, + edition: _, + recursion_limit: _, + }, } = self; extern_prelude.shrink_to_fit(); @@ -583,7 +596,7 @@ impl DefMap { } pub fn recursion_limit(&self) -> Option { - self.recursion_limit + self.data.recursion_limit } } diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index 6567bda709d63..a7abf445918aa 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs @@ -78,7 +78,7 @@ impl DefMap { let name = name.to_smol_str(); let pred = |n: &_| *n == name; - let registered = self.registered_tools.iter().map(SmolStr::as_str); + let registered = self.data.registered_tools.iter().map(SmolStr::as_str); let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred); // FIXME: tool modules can be shadowed by actual modules if is_tool { @@ -86,7 +86,7 @@ impl DefMap { } if segments.len() == 1 { - let mut registered = self.registered_attrs.iter().map(SmolStr::as_str); + let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str); let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred); return is_inert; } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index b431b6f64739c..53e12733412d1 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -98,11 +98,11 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T .collect() } Some(Err(e)) => { - def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str()); + def_map.data.proc_macro_loading_error = Some(e.clone().into_boxed_str()); Vec::new() } None => { - def_map.proc_macro_loading_error = + def_map.data.proc_macro_loading_error = Some("No proc-macros present for crate".to_owned().into_boxed_str()); Vec::new() } @@ -306,7 +306,7 @@ impl DefCollector<'_> { if *attr_name == hir_expand::name![recursion_limit] { if let Some(limit) = attr.string_value() { if let Ok(limit) = limit.parse() { - self.def_map.recursion_limit = Some(limit); + self.def_map.data.recursion_limit = Some(limit); } } continue; @@ -320,17 +320,17 @@ impl DefCollector<'_> { } if *attr_name == hir_expand::name![no_core] { - self.def_map.no_core = true; + self.def_map.data.no_core = true; continue; } if *attr_name == hir_expand::name![no_std] { - self.def_map.no_std = true; + self.def_map.data.no_std = true; continue; } if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { - self.def_map.rustc_coherence_is_core = true; + self.def_map.data.rustc_coherence_is_core = true; continue; } @@ -344,7 +344,7 @@ impl DefCollector<'_> { [name] => Some(name.to_smol_str()), _ => None, }); - self.def_map.unstable_features.extend(features); + self.def_map.data.unstable_features.extend(features); } let attr_is_register_like = *attr_name == hir_expand::name![register_attr] @@ -359,10 +359,10 @@ impl DefCollector<'_> { }; if *attr_name == hir_expand::name![register_attr] { - self.def_map.registered_attrs.push(registered_name.to_smol_str()); + self.def_map.data.registered_attrs.push(registered_name.to_smol_str()); cov_mark::hit!(register_attr); } else { - self.def_map.registered_tools.push(registered_name.to_smol_str()); + self.def_map.data.registered_tools.push(registered_name.to_smol_str()); cov_mark::hit!(register_tool); } } @@ -530,12 +530,12 @@ impl DefCollector<'_> { fn inject_prelude(&mut self) { // See compiler/rustc_builtin_macros/src/standard_library_imports.rs - if self.def_map.no_core { + if self.def_map.data.no_core { // libcore does not get a prelude. return; } - let krate = if self.def_map.no_std { + let krate = if self.def_map.data.no_std { name![core] } else { let std = name![std]; @@ -548,13 +548,13 @@ impl DefCollector<'_> { } }; - let edition = match self.def_map.edition { + let edition = match self.def_map.data.edition { Edition::Edition2015 => name![rust_2015], Edition::Edition2018 => name![rust_2018], Edition::Edition2021 => name![rust_2021], }; - let path_kind = match self.def_map.edition { + let path_kind = match self.def_map.data.edition { Edition::Edition2015 => PathKind::Plain, _ => PathKind::Abs, }; @@ -611,10 +611,11 @@ impl DefCollector<'_> { self.define_proc_macro(def.name.clone(), proc_macro_id); if let ProcMacroKind::CustomDerive { helpers } = def.kind { self.def_map + .data .exported_derives .insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers); } - self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); + self.def_map.data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); } /// Define a macro with `macro_rules`. @@ -773,7 +774,7 @@ impl DefCollector<'_> { fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { let _p = profile::span("resolve_import") .detail(|| format!("{}", import.path.display(self.db.upcast()))); - tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); + tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); if import.is_extern_crate { let name = import .path @@ -1153,7 +1154,7 @@ impl DefCollector<'_> { // Record its helper attributes. if def_id.krate != self.def_map.krate { let def_map = self.db.crate_def_map(def_id.krate); - if let Some(helpers) = def_map.exported_derives.get(&def_id) { + if let Some(helpers) = def_map.data.exported_derives.get(&def_id) { self.def_map .derive_helpers_in_scope .entry(ast_id.ast_id.map(|it| it.upcast())) @@ -2166,6 +2167,7 @@ impl ModCollector<'_, '_> { if let Some(helpers) = helpers_opt { self.def_collector .def_map + .data .exported_derives .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); } diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 751536db3848f..bfba6eab6dfe9 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -218,7 +218,7 @@ impl DefMap { // rust-lang/rust#57745) // FIXME there must be a nicer way to write this condition PathKind::Plain | PathKind::Abs - if self.edition == Edition::Edition2015 + if self.data.edition == Edition::Edition2015 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => { let (_, segment) = match segments.next() { diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index ae509de05633e..f4cca8d68d0ac 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1094,8 +1094,8 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { let krate = db.crate_graph().iter().next().unwrap(); let def_map = db.crate_def_map(krate); - assert_eq!(def_map.exported_derives.len(), 1); - match def_map.exported_derives.values().next() { + assert_eq!(def_map.data.exported_derives.len(), 1); + match def_map.data.exported_derives.values().next() { Some(helpers) => match &**helpers { [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"), _ => unreachable!(), From bdca349573c00542635848a16276272fb0c1acc9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 15:27:05 +0200 Subject: [PATCH 584/806] Arc DefMap::data so the block def maps can share it --- crates/hir-def/src/nameres.rs | 51 ++++++++-------- crates/hir-def/src/nameres/collector.rs | 77 +++++++++++++------------ 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 86ba9052031c6..e900de88dd02e 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -116,10 +116,10 @@ pub struct DefMap { diagnostics: Vec, // FIXME: Arc this so we can share it with block def maps - data: CrateData, + data: Arc, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] struct CrateData { /// Side table for resolving derive helpers. exported_derives: FxHashMap>, @@ -141,6 +141,28 @@ struct CrateData { edition: Edition, recursion_limit: Option, } +impl CrateData { + fn shrink_to_fit(&mut self) { + let Self { + exported_derives, + fn_proc_macro_mapping, + registered_attrs, + registered_tools, + unstable_features, + proc_macro_loading_error: _, + rustc_coherence_is_core: _, + no_core: _, + no_std: _, + edition: _, + recursion_limit: _, + } = self; + exported_derives.shrink_to_fit(); + fn_proc_macro_mapping.shrink_to_fit(); + registered_attrs.shrink_to_fit(); + registered_tools.shrink_to_fit(); + unstable_features.shrink_to_fit(); + } +} /// For `DefMap`s computed for a block expression, this stores its location in the parent map. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -287,6 +309,7 @@ impl DefMap { ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data); + def_map.data = parent_map.data.clone(); def_map.block = Some(BlockInfo { block: block_id, parent: BlockRelativeModuleId { @@ -314,7 +337,7 @@ impl DefMap { prelude: None, modules, diagnostics: Vec::new(), - data: CrateData { + data: Arc::new(CrateData { recursion_limit: None, exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), @@ -326,7 +349,7 @@ impl DefMap { no_core: false, no_std: false, edition, - }, + }), } } @@ -558,32 +581,14 @@ impl DefMap { block: _, krate: _, prelude: _, - data: - CrateData { - exported_derives, - fn_proc_macro_mapping, - registered_attrs, - registered_tools, - unstable_features, - proc_macro_loading_error: _, - rustc_coherence_is_core: _, - no_core: _, - no_std: _, - edition: _, - recursion_limit: _, - }, + data: _, } = self; extern_prelude.shrink_to_fit(); macro_use_prelude.shrink_to_fit(); - exported_derives.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); - registered_attrs.shrink_to_fit(); - registered_tools.shrink_to_fit(); - fn_proc_macro_mapping.shrink_to_fit(); derive_helpers_in_scope.shrink_to_fit(); - unstable_features.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 53e12733412d1..9e35c47d4fbfa 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -86,7 +86,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T let proc_macros = if is_proc_macro { match db.proc_macros().get(&def_map.krate) { Some(Ok(proc_macros)) => { - proc_macros + Ok(proc_macros .iter() .enumerate() .map(|(idx, it)| { @@ -95,20 +95,13 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) }) - .collect() - } - Some(Err(e)) => { - def_map.data.proc_macro_loading_error = Some(e.clone().into_boxed_str()); - Vec::new() - } - None => { - def_map.data.proc_macro_loading_error = - Some("No proc-macros present for crate".to_owned().into_boxed_str()); - Vec::new() + .collect()) } + Some(Err(e)) => Err(e.clone().into_boxed_str()), + None => Err("No proc-macros present for crate".to_owned().into_boxed_str()), } } else { - vec![] + Ok(vec![]) }; let mut collector = DefCollector { @@ -263,7 +256,7 @@ struct DefCollector<'a> { /// built by the build system, and is the list of proc. macros we can actually expand. It is /// empty when proc. macro support is disabled (in which case we still do name resolution for /// them). - proc_macros: Vec<(Name, ProcMacroExpander)>, + proc_macros: Result, Box>, is_proc_macro: bool, from_glob_import: PerNsGlobImports, /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute. @@ -290,6 +283,11 @@ impl DefCollector<'_> { let module_id = DefMap::ROOT; let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); + let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); + + if let Err(e) = &self.proc_macros { + crate_data.proc_macro_loading_error = Some(e.clone()); + } // Process other crate-level attributes. for attr in &*attrs { @@ -306,7 +304,7 @@ impl DefCollector<'_> { if *attr_name == hir_expand::name![recursion_limit] { if let Some(limit) = attr.string_value() { if let Ok(limit) = limit.parse() { - self.def_map.data.recursion_limit = Some(limit); + crate_data.recursion_limit = Some(limit); } } continue; @@ -320,17 +318,17 @@ impl DefCollector<'_> { } if *attr_name == hir_expand::name![no_core] { - self.def_map.data.no_core = true; + crate_data.no_core = true; continue; } if *attr_name == hir_expand::name![no_std] { - self.def_map.data.no_std = true; + crate_data.no_std = true; continue; } if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { - self.def_map.data.rustc_coherence_is_core = true; + crate_data.rustc_coherence_is_core = true; continue; } @@ -344,7 +342,7 @@ impl DefCollector<'_> { [name] => Some(name.to_smol_str()), _ => None, }); - self.def_map.data.unstable_features.extend(features); + crate_data.unstable_features.extend(features); } let attr_is_register_like = *attr_name == hir_expand::name![register_attr] @@ -359,14 +357,15 @@ impl DefCollector<'_> { }; if *attr_name == hir_expand::name![register_attr] { - self.def_map.data.registered_attrs.push(registered_name.to_smol_str()); + crate_data.registered_attrs.push(registered_name.to_smol_str()); cov_mark::hit!(register_attr); } else { - self.def_map.data.registered_tools.push(registered_name.to_smol_str()); + crate_data.registered_tools.push(registered_name.to_smol_str()); cov_mark::hit!(register_tool); } } + crate_data.shrink_to_fit(); self.inject_prelude(); ModCollector { @@ -598,24 +597,29 @@ impl DefCollector<'_> { def: ProcMacroDef, id: ItemTreeId, fn_id: FunctionId, - module_id: ModuleId, ) { + if self.def_map.block.is_some() { + return; + } + let crate_root = self.def_map.module_id(DefMap::ROOT); + let kind = def.kind.to_basedb_kind(); - let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { - Some(&(_, expander)) => (expander, kind), - None => (ProcMacroExpander::dummy(), kind), - }; + let (expander, kind) = + match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) { + Ok(Some(&(_, expander))) => (expander, kind), + _ => (ProcMacroExpander::dummy(), kind), + }; let proc_macro_id = - ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db); + ProcMacroLoc { container: crate_root, id, expander, kind }.intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); + let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); if let ProcMacroKind::CustomDerive { helpers } = def.kind { - self.def_map - .data + crate_data .exported_derives .insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers); } - self.def_map.data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); + crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); } /// Define a macro with `macro_rules`. @@ -1639,12 +1643,10 @@ impl ModCollector<'_, '_> { let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT { if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { - let crate_root = def_map.module_id(DefMap::ROOT); self.def_collector.export_proc_macro( proc_macro, ItemTreeId::new(self.tree_id, id), fn_id, - crate_root, ); } } @@ -2165,11 +2167,12 @@ impl ModCollector<'_, '_> { &self.item_tree[mac.visibility], ); if let Some(helpers) = helpers_opt { - self.def_collector - .def_map - .data - .exported_derives - .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); + if self.def_collector.def_map.block.is_none() { + Arc::get_mut(&mut self.def_collector.def_map.data) + .unwrap() + .exported_derives + .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); + } } } @@ -2277,7 +2280,7 @@ mod tests { unresolved_macros: Vec::new(), mod_dirs: FxHashMap::default(), cfg_options: &CfgOptions::default(), - proc_macros: Default::default(), + proc_macros: Ok(vec![]), from_glob_import: Default::default(), skip_attrs: Default::default(), is_proc_macro: false, From 1e6406e22338af234180071c5b82e390cb43904b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 15:49:05 +0200 Subject: [PATCH 585/806] Move extern prelude into CrateData --- crates/hir-def/src/nameres.rs | 26 +++++++------ crates/hir-def/src/nameres/collector.rs | 39 +++++++++++-------- crates/hir-def/src/nameres/path_resolution.rs | 9 +++-- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index e900de88dd02e..e4693a2b7881e 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -103,8 +103,6 @@ pub struct DefMap { /// but that attribute is nightly and when used in a block, it affects resolution globally /// so we aren't handling this correctly anyways). prelude: Option, - /// The extern prelude is only populated for non-block DefMaps - extern_prelude: FxHashMap, /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that /// this contains all kinds of macro, not just `macro_rules!` macro. macro_use_prelude: FxHashMap, @@ -115,12 +113,13 @@ pub struct DefMap { diagnostics: Vec, - // FIXME: Arc this so we can share it with block def maps data: Arc, } #[derive(Clone, Debug, PartialEq, Eq)] struct CrateData { + extern_prelude: FxHashMap, + /// Side table for resolving derive helpers. exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, @@ -141,9 +140,11 @@ struct CrateData { edition: Edition, recursion_limit: Option, } + impl CrateData { fn shrink_to_fit(&mut self) { let Self { + extern_prelude, exported_derives, fn_proc_macro_mapping, registered_attrs, @@ -156,6 +157,7 @@ impl CrateData { edition: _, recursion_limit: _, } = self; + extern_prelude.shrink_to_fit(); exported_derives.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); registered_attrs.shrink_to_fit(); @@ -181,7 +183,11 @@ struct BlockRelativeModuleId { impl BlockRelativeModuleId { fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc { - ModuleId { krate, block: self.block, local_id: self.local_id }.def_map(db) + self.into_module(krate).def_map(db) + } + + fn into_module(self, krate: CrateId) -> ModuleId { + ModuleId { krate, block: self.block, local_id: self.local_id } } } @@ -330,15 +336,14 @@ impl DefMap { DefMap { _c: Count::new(), block: None, + modules, krate, - extern_prelude: FxHashMap::default(), + prelude: None, macro_use_prelude: FxHashMap::default(), derive_helpers_in_scope: FxHashMap::default(), - prelude: None, - modules, diagnostics: Vec::new(), data: Arc::new(CrateData { - recursion_limit: None, + extern_prelude: FxHashMap::default(), exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, @@ -349,6 +354,7 @@ impl DefMap { no_core: false, no_std: false, edition, + recursion_limit: None, }), } } @@ -412,7 +418,7 @@ impl DefMap { } pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { - self.extern_prelude.iter().map(|(name, def)| (name, *def)) + self.data.extern_prelude.iter().map(|(name, def)| (name, *def)) } pub(crate) fn macro_use_prelude(&self) -> impl Iterator + '_ { @@ -573,7 +579,6 @@ impl DefMap { // Exhaustive match to require handling new fields. let Self { _c: _, - extern_prelude, macro_use_prelude, diagnostics, modules, @@ -584,7 +589,6 @@ impl DefMap { data: _, } = self; - extern_prelude.shrink_to_fit(); macro_use_prelude.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 9e35c47d4fbfa..29ee13648ea5d 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{iter, mem}; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, Dependency, Edition, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -62,7 +62,7 @@ static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128); static FIXED_POINT_LIMIT: Limit = Limit::new(8192); -pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap { +pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap { let crate_graph = db.crate_graph(); let mut deps = FxHashMap::default(); @@ -70,14 +70,8 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T let krate = &crate_graph[def_map.krate]; for dep in &krate.dependencies { tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); - let dep_def_map = db.crate_def_map(dep.crate_id); - let dep_root = dep_def_map.module_id(DefMap::ROOT); - deps.insert(dep.as_name(), dep_root); - - if dep.is_prelude() && !tree_id.is_block() { - def_map.extern_prelude.insert(dep.as_name(), dep_root); - } + deps.insert(dep.as_name(), dep.clone()); } let cfg_options = &krate.cfg_options; @@ -245,7 +239,7 @@ enum MacroDirectiveKind { struct DefCollector<'a> { db: &'a dyn DefDatabase, def_map: DefMap, - deps: FxHashMap, + deps: FxHashMap, glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec, @@ -289,6 +283,15 @@ impl DefCollector<'_> { crate_data.proc_macro_loading_error = Some(e.clone()); } + for (name, dep) in &self.deps { + if dep.is_prelude() { + crate_data.extern_prelude.insert( + name.clone(), + ModuleId { krate: dep.crate_id, block: None, local_id: DefMap::ROOT }, + ); + } + } + // Process other crate-level attributes. for attr in &*attrs { if let Some(cfg) = attr.cfg() { @@ -832,15 +835,16 @@ impl DefCollector<'_> { if *name == name!(self) { cov_mark::hit!(extern_crate_self_as); let root = match self.def_map.block { - Some(_) => { - let def_map = self.def_map.crate_root(self.db).def_map(self.db); - def_map.module_id(DefMap::ROOT) - } + Some(_) => self.def_map.crate_root(self.db), None => self.def_map.module_id(DefMap::ROOT), }; Some(root) } else { - self.deps.get(name).copied() + self.deps.get(name).map(|dep| ModuleId { + krate: dep.crate_id, + block: None, + local_id: DefMap::ROOT, + }) } } @@ -883,7 +887,10 @@ impl DefCollector<'_> { { if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name) { - self.def_map.extern_prelude.insert(name.clone(), def); + Arc::get_mut(&mut self.def_map.data) + .unwrap() + .extern_prelude + .insert(name.clone(), def); } } diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index bfba6eab6dfe9..c5335aaf121e7 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -80,8 +80,8 @@ impl DefMap { name: &Name, ) -> Option { match self.block { - Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(), - None => self.extern_prelude.get(name).copied(), + Some(_) => self.crate_root(db).def_map(db).data.extern_prelude.get(name).copied(), + None => self.data.extern_prelude.get(name).copied(), } } @@ -304,7 +304,7 @@ impl DefMap { Some((_, segment)) => segment, None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; - if let Some(&def) = self.extern_prelude.get(segment) { + if let Some(&def) = self.data.extern_prelude.get(segment) { tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def); PerNs::types(def.into(), Visibility::Public) } else { @@ -453,7 +453,8 @@ impl DefMap { }; let extern_prelude = || { - self.extern_prelude + self.data + .extern_prelude .get(name) .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) }; From 54e3ef658a83dea6a9af282627496114df6dd641 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 15:50:19 +0200 Subject: [PATCH 586/806] Rename nameres::CrateData to DefMapCrateData --- crates/hir-def/src/nameres.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index e4693a2b7881e..b5a30f9d885a0 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -113,11 +113,12 @@ pub struct DefMap { diagnostics: Vec, - data: Arc, + data: Arc, } +/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps. #[derive(Clone, Debug, PartialEq, Eq)] -struct CrateData { +struct DefMapCrateData { extern_prelude: FxHashMap, /// Side table for resolving derive helpers. @@ -141,7 +142,7 @@ struct CrateData { recursion_limit: Option, } -impl CrateData { +impl DefMapCrateData { fn shrink_to_fit(&mut self) { let Self { extern_prelude, @@ -342,7 +343,7 @@ impl DefMap { macro_use_prelude: FxHashMap::default(), derive_helpers_in_scope: FxHashMap::default(), diagnostics: Vec::new(), - data: Arc::new(CrateData { + data: Arc::new(DefMapCrateData { extern_prelude: FxHashMap::default(), exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), From f4c52b40bd867c9aacd84f4890b4a12bba814f69 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 1 Jun 2023 17:51:53 +0330 Subject: [PATCH 587/806] fix bug in labeled for loop desugaring --- crates/hir-def/src/body/lower.rs | 17 +++++++++++-- .../src/handlers/undeclared_label.rs | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index acc9943481a8c..76301782af68f 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -744,10 +744,13 @@ impl ExprCollector<'_> { args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; + let label = e.label().map(|label| self.collect_label(label)); let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, - expr: self.collect_expr_opt(e.loop_body().map(|x| x.into())), + expr: self.with_opt_labeled_rib(label, |this| { + this.collect_expr_opt(e.loop_body().map(|x| x.into())) + }), }; let iter_name = Name::generate_new_name(); let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable); @@ -769,7 +772,6 @@ impl ExprCollector<'_> { Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr.clone(), ); - let label = e.label().map(|label| self.collect_label(label)); let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone()); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -1426,6 +1428,17 @@ impl ExprCollector<'_> { self.label_ribs.pop(); res } + + fn with_opt_labeled_rib( + &mut self, + label: Option, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + match label { + None => f(self), + Some(label) => self.with_labeled_rib(label, f), + } + } // endregion: labels } diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 768efecb086e5..034e4fcfb85b2 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -33,6 +33,31 @@ fn foo() { ); } + #[test] + fn for_loop() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + 'xxx: for _ in unknown { + 'yyy: for _ in unknown { + break 'xxx; + continue 'yyy; + break 'zzz; + //^^^^ error: use of undeclared label `'zzz` + } + continue 'xxx; + continue 'yyy; + //^^^^ error: use of undeclared label `'yyy` + break 'xxx; + break 'yyy; + //^^^^ error: use of undeclared label `'yyy` + } +} +"#, + ); + } + #[test] fn try_operator_desugar_works() { check_diagnostics( From a45fc9465204c9fb8c6792e74e3ed10959e46001 Mon Sep 17 00:00:00 2001 From: Gary <125548724+AnakinSkywalkeer@users.noreply.github.com> Date: Thu, 1 Jun 2023 21:03:42 +0530 Subject: [PATCH 588/806] Update setup.rs --- src/bootstrap/setup.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 40038df833210..9b26d3f0a66c3 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -20,7 +20,7 @@ pub enum Profile { Codegen, Library, Tools, - User, + Dist, None, } @@ -42,7 +42,7 @@ impl Profile { pub fn all() -> impl Iterator { use Profile::*; // N.B. these are ordered by how they are displayed, not alphabetically - [Library, Compiler, Codegen, Tools, User, None].iter().copied() + [Library, Compiler, Codegen, Tools, Dist, None].iter().copied() } pub fn purpose(&self) -> String { @@ -52,7 +52,7 @@ impl Profile { Compiler => "Contribute to the compiler itself", Codegen => "Contribute to the compiler, and also modify LLVM or codegen", Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", - User => "Install Rust from source", + Dist => "Install Rust from source", None => "Do not modify `config.toml`" } .to_string() @@ -72,7 +72,7 @@ impl Profile { Profile::Codegen => "codegen", Profile::Library => "library", Profile::Tools => "tools", - Profile::User => "user", + Profile::Dist => "dist", Profile::None => "none", } } @@ -86,7 +86,7 @@ impl FromStr for Profile { "lib" | "library" => Ok(Profile::Library), "compiler" => Ok(Profile::Compiler), "llvm" | "codegen" => Ok(Profile::Codegen), - "maintainer" | "user" => Ok(Profile::User), + "maintainer" | "dist" => Ok(Profile::Dist), "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { Ok(Profile::Tools) } @@ -159,7 +159,7 @@ pub fn setup(config: &Config, profile: Profile) { "test src/tools/rustfmt", ], Profile::Library => &["check", "build", "test library/std", "doc"], - Profile::User => &["dist", "build"], + Profile::Dist => &["dist", "build"], }; println!(); @@ -169,7 +169,7 @@ pub fn setup(config: &Config, profile: Profile) { println!("- `x.py {}`", cmd); } - if profile != Profile::User { + if profile != Profile::Dist { println!( "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html" ); From 11b937177bbad6afcffebefd7cd81934ae6b0baa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 1 Jun 2023 17:58:08 +0200 Subject: [PATCH 589/806] Skip extern prelude path resolution in block def maps --- crates/hir-def/src/find_path.rs | 4 +-- crates/hir-def/src/nameres.rs | 11 ++----- crates/hir-def/src/nameres/collector.rs | 6 +--- crates/hir-def/src/nameres/path_resolution.rs | 32 +++++++++---------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 1e299fecc9d81..e8cc2eab4617e 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -81,7 +81,7 @@ fn find_path_inner( } let def_map = from.def_map(db); - let crate_root = def_map.crate_root(db); + let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); @@ -454,7 +454,7 @@ fn find_local_import_locations( worklist.push(ancestor); } - let def_map = def_map.crate_root(db).def_map(db); + let def_map = def_map.crate_root().def_map(db); let mut seen: FxHashSet<_> = FxHashSet::default(); diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index b5a30f9d885a0..9b520bc3030f0 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -431,15 +431,8 @@ impl DefMap { ModuleId { krate: self.krate, local_id, block } } - pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { - self.with_ancestor_maps(db, Self::ROOT, &mut |def_map, _module| { - if def_map.block.is_none() { - Some(def_map.module_id(Self::ROOT)) - } else { - None - } - }) - .expect("DefMap chain without root") + pub(crate) fn crate_root(&self) -> ModuleId { + ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT } } pub(crate) fn resolve_path( diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 29ee13648ea5d..06542b4b1e999 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -834,11 +834,7 @@ impl DefCollector<'_> { fn resolve_extern_crate(&self, name: &Name) -> Option { if *name == name!(self) { cov_mark::hit!(extern_crate_self_as); - let root = match self.def_map.block { - Some(_) => self.def_map.crate_root(self.db), - None => self.def_map.module_id(DefMap::ROOT), - }; - Some(root) + Some(self.def_map.crate_root()) } else { self.deps.get(name).map(|dep| ModuleId { krate: dep.crate_id, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index c5335aaf121e7..5f6163175a726 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -20,7 +20,7 @@ use crate::{ path::{ModPath, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, - AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, + AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -74,17 +74,6 @@ impl PerNs { } impl DefMap { - pub(super) fn resolve_name_in_extern_prelude( - &self, - db: &dyn DefDatabase, - name: &Name, - ) -> Option { - match self.block { - Some(_) => self.crate_root(db).def_map(db).data.extern_prelude.get(name).copied(), - None => self.data.extern_prelude.get(name).copied(), - } - } - pub(crate) fn resolve_visibility( &self, db: &dyn DefDatabase, @@ -204,7 +193,7 @@ impl DefMap { PathKind::DollarCrate(krate) => { if krate == self.krate { cov_mark::hit!(macro_dollar_crate_self); - PerNs::types(self.crate_root(db).into(), Visibility::Public) + PerNs::types(self.crate_root().into(), Visibility::Public) } else { let def_map = db.crate_def_map(krate); let module = def_map.module_id(Self::ROOT); @@ -212,7 +201,7 @@ impl DefMap { PerNs::types(module.into(), Visibility::Public) } } - PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public), + PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) @@ -453,6 +442,10 @@ impl DefMap { }; let extern_prelude = || { + if self.block.is_some() { + // Don't resolve extern prelude in block `DefMap`s. + return PerNs::none(); + } self.data .extern_prelude .get(name) @@ -479,13 +472,20 @@ impl DefMap { ) -> PerNs { let from_crate_root = match self.block { Some(_) => { - let def_map = self.crate_root(db).def_map(db); + let def_map = self.crate_root().def_map(db); def_map[Self::ROOT].scope.get(name) } None => self[Self::ROOT].scope.get(name), }; let from_extern_prelude = || { - self.resolve_name_in_extern_prelude(db, name) + if self.block.is_some() { + // Don't resolve extern prelude in block `DefMap`s. + return PerNs::none(); + } + self.data + .extern_prelude + .get(name) + .copied() .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) }; From 96892a52edc4ae063a1cd2ab5b92be1df2fc65ea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 1 Jun 2023 18:29:18 +0200 Subject: [PATCH 590/806] Fix bug where private item with intermediate doc hidden re-export was not inlined --- src/librustdoc/clean/mod.rs | 3 ++- src/librustdoc/visit_ast.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5fd867189fd71..60da1d6577490 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2180,7 +2180,8 @@ fn get_all_import_attributes<'hir>( // This is the "original" reexport so we get all its attributes without filtering them. attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); first = false; - } else { + // We don't add attributes of an intermediate re-export if it has `#[doc(hidden)]`. + } else if !cx.tcx.is_doc_hidden(def_id) { add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id)); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index abb9229fbd51a..1689445b9ef7c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -246,7 +246,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { glob: bool, please_inline: bool, ) -> bool { - debug!("maybe_inline_local res: {:?}", res); + debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}"); if renamed == Some(kw::Underscore) { // We never inline `_` reexports. @@ -308,6 +308,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .cache .effective_visibilities .is_directly_public(tcx, item_def_id.to_def_id()) && + !tcx.is_doc_hidden(item_def_id) && !inherits_doc_hidden(tcx, item_def_id, None) { // The imported item is public and not `doc(hidden)` so no need to inline it. From a825b1e5da8bce093abf77559cf6b82b590a254e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 1 Jun 2023 18:35:00 +0200 Subject: [PATCH 591/806] Add regression test where private item with intermediate doc hidden re-export was not inlined --- ...ne-private-with-intermediate-doc-hidden.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs diff --git a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs new file mode 100644 index 0000000000000..e382940a47eb8 --- /dev/null +++ b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs @@ -0,0 +1,23 @@ +// This test ensures that if a private item is re-exported with an intermediate +// `#[doc(hidden)]` re-export, it'll still be inlined (and not include any attribute +// from the doc hidden re-export. + +#![crate_name = "foo"] + +// @has 'foo/index.html' +// There should only be one struct displayed. +// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 1 +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs' +// @has - '//*[@id="main-content"]//a[@href="struct.Reexport.html"]' 'Reexport' +// @has - '//*[@id="main-content"]//*[@class="desc docblock-short"]' 'Visible. Original.' + +mod private { + /// Original. + pub struct Bar3; +} + +/// Hidden. +#[doc(hidden)] +pub use crate::private::Bar3; +/// Visible. +pub use self::Bar3 as Reexport; From cd4354578a9a8cae62d9340eda66ec6689ac8ba7 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 1 Jun 2023 13:07:51 -0400 Subject: [PATCH 592/806] Avoid passing --cpu-features when empty Added in 12ac719b99560072cbe52a957f22d3fe6946cf2a, this logic always passed --cpu-features, even when the value was the empty string. --- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f8bb9bf2bb50b..8dbd4456a6b89 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2256,11 +2256,13 @@ fn add_order_independent_options( } else if flavor == LinkerFlavor::Bpf { cmd.arg("--cpu"); cmd.arg(&codegen_results.crate_info.target_cpu); - cmd.arg("--cpu-features"); - cmd.arg(match &sess.opts.cg.target_feature { - feat if !feat.is_empty() => feat.as_ref(), - _ => sess.target.options.features.as_ref(), - }); + if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features] + .into_iter() + .find(|feat| !feat.is_empty()) + { + cmd.arg("--cpu-features"); + cmd.arg(feat); + } } cmd.linker_plugin_lto(); From a647ba250a65b44574830cb71aab5c0403adf31b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:39:14 +0100 Subject: [PATCH 593/806] Remember names of `cfg`-ed out items to mention them in diagnostics `#[cfg]`s are frequently used to gate crate content behind cargo features. This can lead to very confusing errors when features are missing. For example, `serde` doesn't have the `derive` feature by default. Therefore, `serde::Serialize` fails to resolve with a generic error, even though the macro is present in the docs. This commit adds a list of all stripped item names to metadata. This is filled during macro expansion and then, through a fed query, persisted in metadata. The downstream resolver can then access the metadata to look at possible candidates for mentioning in the errors. This slightly increases metadata (800k->809k for the feature-heavy windows crate), but not enough to really matter. --- compiler/rustc_ast/src/expand/mod.rs | 17 ++++ compiler/rustc_expand/src/base.rs | 4 +- compiler/rustc_expand/src/config.rs | 20 ++-- compiler/rustc_expand/src/expand.rs | 49 +++++++++- compiler/rustc_metadata/src/rmeta/decoder.rs | 9 ++ .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 13 +++ compiler/rustc_metadata/src/rmeta/mod.rs | 2 + compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 11 ++- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_resolve/src/diagnostics.rs | 69 ++++++++++++-- compiler/rustc_resolve/src/errors.rs | 4 + compiler/rustc_resolve/src/ident.rs | 92 ++++++++++--------- compiler/rustc_resolve/src/imports.rs | 26 +++++- compiler/rustc_resolve/src/late.rs | 24 ++++- .../rustc_resolve/src/late/diagnostics.rs | 37 ++++++-- compiler/rustc_resolve/src/lib.rs | 27 +++++- compiler/rustc_resolve/src/macros.rs | 12 ++- tests/ui/cfg/auxiliary/cfged_out.rs | 22 +++++ tests/ui/cfg/diagnostics-cross-crate.rs | 31 +++++++ tests/ui/cfg/diagnostics-cross-crate.stderr | 53 +++++++++++ tests/ui/cfg/diagnostics-not-a-def.rs | 12 +++ tests/ui/cfg/diagnostics-not-a-def.stderr | 9 ++ tests/ui/cfg/diagnostics-reexport.rs | 16 ++++ tests/ui/cfg/diagnostics-reexport.stderr | 15 +++ tests/ui/cfg/diagnostics-same-crate.rs | 51 ++++++++++ tests/ui/cfg/diagnostics-same-crate.stderr | 47 ++++++++++ tests/ui/macros/builtin-std-paths-fail.stderr | 3 + tests/ui/macros/macro-outer-attributes.stderr | 5 + 30 files changed, 599 insertions(+), 84 deletions(-) create mode 100644 tests/ui/cfg/auxiliary/cfged_out.rs create mode 100644 tests/ui/cfg/diagnostics-cross-crate.rs create mode 100644 tests/ui/cfg/diagnostics-cross-crate.stderr create mode 100644 tests/ui/cfg/diagnostics-not-a-def.rs create mode 100644 tests/ui/cfg/diagnostics-not-a-def.stderr create mode 100644 tests/ui/cfg/diagnostics-reexport.rs create mode 100644 tests/ui/cfg/diagnostics-reexport.stderr create mode 100644 tests/ui/cfg/diagnostics-same-crate.rs create mode 100644 tests/ui/cfg/diagnostics-same-crate.stderr diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 2ee1bfe0ae71b..942347383ce31 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,3 +1,20 @@ //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. +use rustc_span::{def_id::DefId, symbol::Ident}; + +use crate::MetaItem; + pub mod allocator; + +#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] +pub struct StrippedCfgItem { + pub parent_module: ModId, + pub name: Ident, + pub cfg: MetaItem, +} + +impl StrippedCfgItem { + pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { + StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg } + } +} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0d43b30474b06..b7c30841983d7 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -947,6 +947,8 @@ pub trait ResolverExpand { /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem); + /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; } @@ -965,7 +967,7 @@ pub trait LintStoreExpand { type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. pub mod_path: Vec, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 4ff8e409d88e3..690f80f6876e4 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -416,20 +416,28 @@ impl<'a> StripUnconfigured<'a> { /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) + attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0) } - pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool { + pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option) { let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { Ok(meta_item) => meta_item, Err(mut err) => { err.emit(); - return true; + return (true, None); } }; - parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { - attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features) - }) + ( + parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { + attr::cfg_matches( + &meta_item, + &self.sess.parse_sess, + self.lint_node_id, + self.features, + ) + }), + Some(meta_item), + ) } /// If attributes are not allowed on expressions, emit an error for `attr` diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ce0093c7d4c0e..dd8863df1953c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1042,6 +1042,12 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) { collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } + + /// All of the names (items) declared by this node. + /// This is an approximation and should only be used for diagnostics. + fn declared_names(&self) -> Vec { + vec![] + } } impl InvocationCollectorNode for P { @@ -1148,6 +1154,27 @@ impl InvocationCollectorNode for P { collector.cx.current_expansion.module = orig_module; res } + fn declared_names(&self) -> Vec { + if let ItemKind::Use(ut) = &self.kind { + fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { + match &ut.kind { + ast::UseTreeKind::Glob => {} + ast::UseTreeKind::Simple(_) => idents.push(ut.ident()), + ast::UseTreeKind::Nested(nested) => { + for (ut, _) in nested { + collect_use_tree_leaves(&ut, idents); + } + } + } + } + + let mut idents = Vec::new(); + collect_use_tree_leaves(&ut, &mut idents); + return idents; + } + + vec![self.ident] + } } struct TraitItemTag; @@ -1685,8 +1712,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { node: &mut impl HasAttrs, attr: ast::Attribute, pos: usize, - ) -> bool { - let res = self.cfg().cfg_true(&attr); + ) -> (bool, Option) { + let (res, meta_item) = self.cfg().cfg_true(&attr); if res { // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, // and some tools like rustdoc and clippy rely on that. Find a way to remove them @@ -1694,7 +1721,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.expanded_inert_attrs.mark(&attr); node.visit_attrs(|attrs| attrs.insert(pos, attr)); } - res + + (res, meta_item) } fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { @@ -1715,9 +1743,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { return match self.take_first_attr(&mut node) { Some((attr, pos, derives)) => match attr.name_or_empty() { sym::cfg => { - if self.expand_cfg_true(&mut node, attr, pos) { + let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos); + if res { continue; } + + if let Some(meta_item) = meta_item { + for name in node.declared_names() { + self.cx.resolver.append_stripped_cfg_item( + self.cx.current_expansion.lint_node_id, + name, + meta_item.clone(), + ) + } + } Default::default() } sym::cfg_attr => { @@ -1761,7 +1800,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { Some((attr, pos, derives)) => match attr.name_or_empty() { sym::cfg => { let span = attr.span; - if self.expand_cfg_true(node, attr, pos) { + if self.expand_cfg_true(node, attr, pos).0 { continue; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 8f883bdcf12b8..21cbab542933c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -995,6 +995,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } + fn get_stripped_cfg_items(self, cnum: CrateNum, tcx: TyCtxt<'tcx>) -> &'tcx [StrippedCfgItem] { + let item_names = self + .root + .stripped_cfg_items + .decode((self, tcx)) + .map(|item| item.map_mod_id(|index| DefId { krate: cnum, index })); + tcx.arena.alloc_from_iter(item_names) + } + /// Iterates over the diagnostic items in the given crate. fn get_diagnostic_items(self) -> DiagnosticItems { let mut id_to_name = FxHashMap::default(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a15307e4345c1..2a609303197d6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -345,6 +345,7 @@ provide! { tcx, def_id, other, cdata, stability_implications => { cdata.get_stability_implications(tcx).iter().copied().collect() } + stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6ceb61e793e53..8f63a0c9df629 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef; use crate::rmeta::table::TableBuilder; use crate::rmeta::*; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -584,6 +585,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { (self.encode_lang_items(), self.encode_lang_items_missing()) }); + let stripped_cfg_items = stat!("stripped-cfg-items", || self.encode_stripped_cfg_items()); + let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items()); let native_libraries = stat!("native-libs", || self.encode_native_libraries()); @@ -694,6 +697,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { lang_items, diagnostic_items, lang_items_missing, + stripped_cfg_items, native_libraries, foreign_modules, source_map, @@ -1940,6 +1944,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(&tcx.lang_items().missing) } + fn encode_stripped_cfg_items(&mut self) -> LazyArray> { + self.lazy_array( + self.tcx + .stripped_cfg_items(LOCAL_CRATE) + .into_iter() + .map(|item| item.clone().map_mod_id(|def_id| def_id.index)), + ) + } + fn encode_traits(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2da888f4468c0..f6bbc4bc60b66 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use table::TableBuilder; use rustc_ast as ast; +use rustc_ast::expand::StrippedCfgItem; use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_hir as hir; @@ -256,6 +257,7 @@ pub(crate) struct CrateRoot { stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, LangItem)>, lang_items_missing: LazyArray, + stripped_cfg_items: LazyArray>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, native_libraries: LazyArray, foreign_modules: LazyArray, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a149a61ec136e..6c404fbb7c684 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -124,6 +124,7 @@ macro_rules! arena_types { [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), + [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, ]); ) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0b31c9bbf8149..bcffbed8d94cd 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -54,7 +54,7 @@ use crate::ty::{ }; use rustc_arena::TypedArena; use rustc_ast as ast; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::{allocator::AllocatorKind, StrippedCfgItem}; use rustc_attr as attr; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; @@ -2173,6 +2173,15 @@ rustc_queries! { query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> { desc { "check whether two const param are definitely not equal to eachother"} } + + /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. + /// Should not be called for the local crate before the resolver outputs are created, as it + /// is only fed there. + query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] { + feedable + desc { "getting cfg-ed out item names" } + separate_provide_extern + } } rustc_query_append! { define_callbacks! } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index a2e77d9cdfe38..13be15269f4c2 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! { ty::fast_reject::SimplifiedType, rustc_ast::Attribute, rustc_ast::DelimArgs, + rustc_ast::expand::StrippedCfgItem, rustc_attr::ConstStability, rustc_attr::DefaultBodyStability, rustc_attr::Deprecation, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 377652ce71bb2..15c8a690530e7 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,8 +1,10 @@ use std::ptr; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; +use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -776,7 +778,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .tcx .sess .create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }), - ResolutionError::FailedToResolve { label, suggestion } => { + ResolutionError::FailedToResolve { last_segment, label, suggestion, module } => { let mut err = struct_span_err!(self.tcx.sess, span, E0433, "failed to resolve: {}", &label); err.span_label(span, label); @@ -789,6 +791,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.multipart_suggestion(msg, suggestions, applicability); } + if let Some(ModuleOrUniformRoot::Module(module)) = module + && let Some(module) = module.opt_def_id() + && let Some(last_segment) = last_segment + { + self.find_cfg_stripped(&mut err, &last_segment, module); + } + err } ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { @@ -971,9 +980,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { VisResolutionError::AncestorOnly(span) => { self.tcx.sess.create_err(errs::AncestorOnly(span)) } - VisResolutionError::FailedToResolve(span, label, suggestion) => { - self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) - } + VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module: None, + }, + ), VisResolutionError::ExpectedFound(span, path_str, res) => { self.tcx.sess.create_err(errs::ExpectedFound { span, res, path_str }) } @@ -1721,10 +1736,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, module: Option>, - i: usize, + failed_segment_idx: usize, ident: Ident, ) -> (String, Option) { - let is_last = i == path.len() - 1; + let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let module_res = match module { Some(ModuleOrUniformRoot::Module(module)) => module.res(), @@ -1758,8 +1773,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else { (format!("could not find `{ident}` in the crate root"), None) } - } else if i > 0 { - let parent = path[i - 1].ident.name; + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; let parent = match parent { // ::foo is mounted at the crate root for 2015, and is the extern // prelude for 2018+ @@ -2207,6 +2222,44 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None } } + + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped( + &mut self, + err: &mut Diagnostic, + last_segment: &Symbol, + module: DefId, + ) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; + + for &StrippedCfgItem { parent_module, name, ref cfg } in symbols { + if parent_module != module || name.name != *last_segment { + continue; + } + + err.span_note(name.span, "found an item that was configured out"); + + if let MetaItemKind::List(nested) = &cfg.kind + && let NestedMetaItem::MetaItem(meta_item) = &nested[0] + && let MetaItemKind::NameValue(feature_name) = &meta_item.kind + { + err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol)); + } + } + } } /// Given a `binding_span` of a binding within a use statement: diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 2ab55f12637c8..e88cbb955b556 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -330,6 +330,7 @@ pub(crate) struct ParamInTyOfConstParam { pub(crate) param_kind: Option, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInTyOfConstParam { #[note(resolve_type_param_in_ty_of_const_param)] @@ -365,6 +366,7 @@ pub(crate) struct ParamInNonTrivialAnonConst { #[help(resolve_param_in_non_trivial_anon_const_help)] pub(crate) struct ParamInNonTrivialAnonConstHelp; +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInNonTrivialAnonConst { #[note(resolve_type_param_in_non_trivial_anon_const)] @@ -562,6 +564,7 @@ pub(crate) struct CfgAccessibleUnsure { pub(crate) span: Span, } +#[derive(Debug)] #[derive(Diagnostic)] #[diag(resolve_param_in_enum_discriminant)] pub(crate) struct ParamInEnumDiscriminant { @@ -573,6 +576,7 @@ pub(crate) struct ParamInEnumDiscriminant { pub(crate) param_kind: ParamKindInEnumDiscriminant, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInEnumDiscriminant { #[note(resolve_type_param_in_enum_discriminant)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 945c7ce3a9b32..ec0a8535e7180 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1365,20 +1365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, ) -> PathResult<'a> { - debug!( - "resolve_path(path={:?}, opt_ns={:?}, finalize={:?}) path_len: {}", - path, - opt_ns, - finalize, - path.len() - ); - let mut module = None; let mut allow_super = true; let mut second_binding = None; - for (i, &Segment { ident, id, .. }) in path.iter().enumerate() { - debug!("resolve_path ident {} {:?} {:?}", i, ident, id); + for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() { + debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id); let record_segment_res = |this: &mut Self, res| { if finalize.is_some() { if let Some(id) = id { @@ -1390,7 +1382,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } }; - let is_last = i + 1 == path.len(); + let is_last = segment_idx + 1 == path.len(); let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let name = ident.name; @@ -1399,7 +1391,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if ns == TypeNS { if allow_super && name == kw::Super { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - let self_module = match i { + let self_module = match segment_idx { 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), _ => match module { Some(ModuleOrUniformRoot::Module(module)) => Some(module), @@ -1414,11 +1406,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { continue; } } - return PathResult::failed(ident.span, false, finalize.is_some(), || { - ("there are too many leading `super` keywords".to_string(), None) - }); + return PathResult::failed( + ident.span, + false, + finalize.is_some(), + module, + || ("there are too many leading `super` keywords".to_string(), None), + ); } - if i == 0 { + if segment_idx == 0 { if name == kw::SelfLower { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); module = Some(ModuleOrUniformRoot::Module( @@ -1447,14 +1443,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // Report special messages for path segment keywords in wrong positions. - if ident.is_path_segment_keyword() && i != 0 { - return PathResult::failed(ident.span, false, finalize.is_some(), || { + if ident.is_path_segment_keyword() && segment_idx != 0 { + return PathResult::failed(ident.span, false, finalize.is_some(), module, || { let name_str = if name == kw::PathRoot { "crate root".to_string() } else { format!("`{}`", name) }; - let label = if i == 1 && path[0].ident.name == kw::PathRoot { + let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { format!("global paths cannot start with {}", name_str) } else { format!("{} in paths can only be used in start position", name_str) @@ -1519,7 +1515,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; match binding { Ok(binding) => { - if i == 1 { + if segment_idx == 1 { second_binding = Some(binding); } let res = binding.res(); @@ -1543,17 +1539,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { record_segment_res(self, res); return PathResult::NonModule(PartialRes::with_unresolved_segments( res, - path.len() - i - 1, + path.len() - segment_idx - 1, )); } else { - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let label = format!( - "`{ident}` is {} {}, not a module", - res.article(), - res.descr() - ); - (label, None) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + let label = format!( + "`{ident}` is {} {}, not a module", + res.article(), + res.descr() + ); + (label, None) + }, + ); } } Err(Undetermined) => return PathResult::Indeterminate, @@ -1562,23 +1564,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if opt_ns.is_some() && !module.is_normal() { return PathResult::NonModule(PartialRes::with_unresolved_segments( module.res().unwrap(), - path.len() - i, + path.len() - segment_idx, )); } } - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - self.report_path_resolution_error( - path, - opt_ns, - parent_scope, - ribs, - ignore_binding, - module, - i, - ident, - ) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + self.report_path_resolution_error( + path, + opt_ns, + parent_scope, + ribs, + ignore_binding, + module, + segment_idx, + ident, + ) + }, + ); } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c1bb262c0d407..7f944fb574596 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -803,14 +803,34 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); - self.report_error(span, ResolutionError::FailedToResolve { label, suggestion }); + self.report_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + ); } return None; } - PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: true, + span, + label, + suggestion, + .. + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); let err = match self.make_path_suggestion( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e0611907613c4..ddd75ea3b33e0 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3524,7 +3524,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { None }; - this.r.use_injections.push(UseError { + let ue = UseError { err, candidates, def_id, @@ -3532,7 +3532,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { suggestion, path: path.into(), is_call: source.is_call(), - }); + }; + + this.r.use_injections.push(ue); } PartialRes::new(Res::Err) @@ -3866,8 +3868,22 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { PartialRes::new(module.res().unwrap()) } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { - return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion })); + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { + return Err(respan( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + )); } PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None), PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f79f8d0c6ca4e..2f9759a668bbe 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -149,6 +149,7 @@ struct BaseError { span_label: Option<(Span, &'static str)>, could_be_expr: bool, suggestion: Option<(Span, &'static str, String)>, + module: Option, } #[derive(Debug)] @@ -210,10 +211,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { _ => false, }, suggestion: None, + module: None, } } else { let item_span = path.last().unwrap().ident.span; - let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { + let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); let suggestion = if self.current_trait_ref.is_none() @@ -247,26 +249,37 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else { None }; - (String::new(), "this scope".to_string(), suggestion) + (String::new(), "this scope".to_string(), None, suggestion) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { if self.r.tcx.sess.edition() > Edition::Edition2015 { // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude // which overrides all other expectations of item type expected = "crate"; - (String::new(), "the list of imported crates".to_string(), None) + (String::new(), "the list of imported crates".to_string(), None, None) } else { - (String::new(), "the crate root".to_string(), None) + ( + String::new(), + "the crate root".to_string(), + Some(CRATE_DEF_ID.to_def_id()), + None, + ) } } else if path.len() == 2 && path[0].ident.name == kw::Crate { - (String::new(), "the crate root".to_string(), None) + (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) { + let mod_res = self.resolve_path(mod_path, Some(TypeNS), None); + let mod_prefix = match mod_res { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, - } - .map_or_else(String::new, |res| format!("{} ", res.descr())); - (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) + }; + + let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id); + + let mod_prefix = + mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr()))); + + (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None) }; let (fallback_label, suggestion) = if path_str == "async" @@ -300,6 +313,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { span_label: None, could_be_expr: false, suggestion, + module, } } } @@ -315,6 +329,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec) { debug!(?res, ?source); let base_error = self.make_base_error(path, span, source, res); + let code = source.error_code(res.is_some()); let mut err = self.r.tcx.sess.struct_span_err_with_code( base_error.span, @@ -366,6 +381,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } self.err_code_special_cases(&mut err, source, path, span); + if let Some(module) = base_error.module { + self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module); + } + (err, candidates) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index fd977e8e254a2..dd8d01e35e5de 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -25,6 +25,7 @@ use errors::{ ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst, ParamKindInTyOfConstParam, }; use rustc_arena::{DroplessArena, TypedArena}; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, attr, NodeId, CRATE_NODE_ID}; use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path}; @@ -171,6 +172,7 @@ enum ImplTraitContext { Universal(LocalDefId), } +#[derive(Debug)] struct BindingError { name: Symbol, origin: BTreeSet, @@ -178,6 +180,7 @@ struct BindingError { could_be_path: bool, } +#[derive(Debug)] enum ResolutionError<'a> { /// Error E0401: can't use type or const parameters from outer function. GenericParamsFromOuterFunction(Res, HasGenericParams), @@ -207,7 +210,12 @@ enum ResolutionError<'a> { /// Error E0431: `self` import can only appear in an import list with a non-empty prefix. SelfImportOnlyInImportListWithNonEmptyPrefix, /// Error E0433: failed to resolve. - FailedToResolve { label: String, suggestion: Option }, + FailedToResolve { + last_segment: Option, + label: String, + suggestion: Option, + module: Option>, + }, /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, /// Error E0435: attempt to use a non-constant value in a constant. @@ -402,6 +410,7 @@ enum PathResult<'a> { label: String, suggestion: Option, is_error_from_last_segment: bool, + module: Option>, }, } @@ -410,11 +419,12 @@ impl<'a> PathResult<'a> { span: Span, is_error_from_last_segment: bool, finalize: bool, + module: Option>, label_and_suggestion: impl FnOnce() -> (String, Option), ) -> PathResult<'a> { let (label, suggestion) = if finalize { label_and_suggestion() } else { (String::new(), None) }; - PathResult::Failed { span, label, suggestion, is_error_from_last_segment } + PathResult::Failed { span, label, suggestion, is_error_from_last_segment, module } } } @@ -685,6 +695,7 @@ struct PrivacyError<'a> { dedup_span: Span, } +#[derive(Debug)] struct UseError<'a> { err: DiagnosticBuilder<'a, ErrorGuaranteed>, /// Candidates which user could `use` to access the missing type. @@ -1059,6 +1070,9 @@ pub struct Resolver<'a, 'tcx> { /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, + /// Names of items that were stripped out via cfg with their corresponding cfg meta item. + stripped_cfg_items: Vec>, + effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, @@ -1353,6 +1367,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { proc_macros: Default::default(), confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), + stripped_cfg_items: Default::default(), effective_visibilities: Default::default(), doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), @@ -1410,6 +1425,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; let effective_visibilities = self.effective_visibilities; + + self.tcx.feed_local_crate().stripped_cfg_items(self.tcx.arena.alloc_from_iter( + self.stripped_cfg_items.into_iter().filter_map(|item| { + let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg }) + }), + )); + let global_ctxt = ResolverGlobalCtxt { expn_that_defined, visibilities, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d8a7bcbfff955..805c804e5759f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -6,6 +6,7 @@ use crate::Namespace::*; use crate::{BuiltinMacroState, Determinacy}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, attr, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; @@ -465,6 +466,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.proc_macros.push(id) } + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) { + self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg }); + } + fn registered_tools(&self) -> &RegisteredTools { &self.registered_tools } @@ -721,7 +726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; - let (span, label) = if let PathResult::Failed { span, label, .. } = path_res { + let (span, label, module) = if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) && partial_res.unresolved_segments() == 0 { @@ -733,7 +738,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect )); } - (span, label) + (span, label, module) } else { ( path_span, @@ -742,11 +747,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind.article(), kind.descr() ), + None, ) }; self.report_error( span, - ResolutionError::FailedToResolve { label, suggestion }, + ResolutionError::FailedToResolve { last_segment: path.last().map(|segment| segment.ident.name), label, suggestion, module }, ); } PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), diff --git a/tests/ui/cfg/auxiliary/cfged_out.rs b/tests/ui/cfg/auxiliary/cfged_out.rs new file mode 100644 index 0000000000000..f6a9089cf29de --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfged_out.rs @@ -0,0 +1,22 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} diff --git a/tests/ui/cfg/diagnostics-cross-crate.rs b/tests/ui/cfg/diagnostics-cross-crate.rs new file mode 100644 index 0000000000000..d2725c94b083b --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.rs @@ -0,0 +1,31 @@ +// aux-build:cfged_out.rs + +extern crate cfged_out; + +fn main() { + // There is no uwu at this path - no diagnostic. + cfged_out::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `cfged_out` + + // It does exist here - diagnostic. + cfged_out::inner::uwu(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out::inner` + + // The module isn't found - we would like to get a diagnostic, but currently don't due to + // the awkward way the resolver diagnostics are currently implemented. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + cfged_out::inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~^ NOTE could not find `doesnt_exist` in `inner` + + // It should find the one in the right module, not the wrong one. + cfged_out::inner::right::meow(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out::inner::right + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - diagnostic. + cfged_out::vanished(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out` +} diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr new file mode 100644 index 0000000000000..046929bc26023 --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -0,0 +1,53 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-cross-crate.rs:18:23 + | +LL | cfged_out::inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:7:16 + | +LL | cfged_out::uwu(); + | ^^^ not found in `cfged_out` + +error[E0425]: cannot find function `uwu` in module `cfged_out::inner` + --> $DIR/diagnostics-cross-crate.rs:11:23 + | +LL | cfged_out::inner::uwu(); + | ^^^ not found in `cfged_out::inner` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` + --> $DIR/diagnostics-cross-crate.rs:22:30 + | +LL | cfged_out::inner::right::meow(); + | ^^^^ not found in `cfged_out::inner::right` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:17:16 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `vanished` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:28:16 + | +LL | cfged_out::vanished(); + | ^^^^^^^^ not found in `cfged_out` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:22:8 + | +LL | pub fn vanished() {} + | ^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-not-a-def.rs b/tests/ui/cfg/diagnostics-not-a-def.rs new file mode 100644 index 0000000000000..7293947122675 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.rs @@ -0,0 +1,12 @@ +pub mod inner { + pub fn i_am_here() { + #[cfg(feature = "another one that doesn't exist")] + loop {} + } +} + +fn main() { + inner::i_am_here(); + // ensure that nothing bad happens when we are checking for cfgs + inner::i_am_not(); //~ ERROR cannot find function +} diff --git a/tests/ui/cfg/diagnostics-not-a-def.stderr b/tests/ui/cfg/diagnostics-not-a-def.stderr new file mode 100644 index 0000000000000..af0e1a1727579 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `i_am_not` in module `inner` + --> $DIR/diagnostics-not-a-def.rs:11:12 + | +LL | inner::i_am_not(); + | ^^^^^^^^ not found in `inner` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-reexport.rs b/tests/ui/cfg/diagnostics-reexport.rs new file mode 100644 index 0000000000000..1d43d6ba02f56 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.rs @@ -0,0 +1,16 @@ +pub mod inner { + #[cfg(FALSE)] + mod gone { + pub fn uwu() {} + } + + #[cfg(FALSE)] + pub use super::uwu; + //~^ NOTE found an item that was configured out +} + +fn main() { + // There is no uwu at this path - no diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `inner` +} diff --git a/tests/ui/cfg/diagnostics-reexport.stderr b/tests/ui/cfg/diagnostics-reexport.stderr new file mode 100644 index 0000000000000..6c977cbfa4172 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-reexport.rs:14:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport.rs:8:20 + | +LL | pub use super::uwu; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-same-crate.rs b/tests/ui/cfg/diagnostics-same-crate.rs new file mode 100644 index 0000000000000..f76ace06a762d --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.rs @@ -0,0 +1,51 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + //~^ NOTE found an item that was configured out + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + //~^ NOTE found an item that was configured out + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} + +fn main() { + // There is no uwu at this path - no diagnostic. + uwu(); //~ ERROR cannot find function + //~^ NOTE not found in this scope + + // It does exist here - diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~| NOTE not found in `inner` + + // The module isn't found - we would like to get a diagnostic, but currently don't due to + // the awkward way the resolver diagnostics are currently implemented. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~| NOTE could not find `doesnt_exist` in `inner` + + // It should find the one in the right module, not the wrong one. + inner::right::meow(); //~ ERROR cannot find function + //~| NOTE not found in `inner::right + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - we would generally want a diagnostic, + // but currently don't have one. + // Not that it matters much though, this is highly unlikely to confuse anyone. + vanished(); //~ ERROR cannot find function + //~^ NOTE not found in this scope +} diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr new file mode 100644 index 0000000000000..30ee6479bd26c --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -0,0 +1,47 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-same-crate.rs:38:12 + | +LL | inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-same-crate.rs:32:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `inner::right` + --> $DIR/diagnostics-same-crate.rs:42:19 + | +LL | inner::right::meow(); + | ^^^^ not found in `inner::right` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:18:16 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `uwu` in this scope + --> $DIR/diagnostics-same-crate.rs:28:5 + | +LL | uwu(); + | ^^^ not found in this scope + +error[E0425]: cannot find function `vanished` in this scope + --> $DIR/diagnostics-same-crate.rs:49:5 + | +LL | vanished(); + | ^^^^^^^^ not found in this scope + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/macros/builtin-std-paths-fail.stderr b/tests/ui/macros/builtin-std-paths-fail.stderr index ba6261011907c..004a39043b778 100644 --- a/tests/ui/macros/builtin-std-paths-fail.stderr +++ b/tests/ui/macros/builtin-std-paths-fail.stderr @@ -93,6 +93,9 @@ error[E0433]: failed to resolve: could not find `test` in `std` | LL | #[std::test] | ^^^^ could not find `test` in `std` + | +note: found an item that was configured out + --> $SRC_DIR/std/src/lib.rs:LL:COL error: aborting due to 16 previous errors diff --git a/tests/ui/macros/macro-outer-attributes.stderr b/tests/ui/macros/macro-outer-attributes.stderr index 0bdc3416f807e..0418e6116041b 100644 --- a/tests/ui/macros/macro-outer-attributes.stderr +++ b/tests/ui/macros/macro-outer-attributes.stderr @@ -4,6 +4,11 @@ error[E0425]: cannot find function `bar` in module `a` LL | a::bar(); | ^^^ not found in `a` | +note: found an item that was configured out + --> $DIR/macro-outer-attributes.rs:9:14 + | +LL | pub fn bar() { }); + | ^^^ help: consider importing this function | LL + use b::bar; From bbf41279fad5bf1604b9cf3f9575c76a00254114 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 16 May 2023 19:58:54 +0100 Subject: [PATCH 594/806] Require that const param tys implement ConstParamTy --- .../rustc_hir_analysis/src/check/wfcheck.rs | 91 +++---------------- library/core/src/mem/transmutability.rs | 5 + 2 files changed, 19 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index fff417fcb2909..5a5a405d9e7a8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -829,83 +829,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(param.def_id).subst_identity(); if tcx.features().adt_const_params { - if let Some(non_structural_match_ty) = - traits::search_for_adt_const_param_violation(param.span, tcx, ty) - { - // We use the same error code in both branches, because this is really the same - // issue: we just special-case the message for type parameters to make it - // clearer. - match non_structural_match_ty.kind() { - ty::Param(_) => { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ) - .span_label( - hir_ty.span, - format!("`{ty}` may not derive both `PartialEq` and `Eq`"), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } - ty::Float(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is forbidden as the type of a const generic parameter", - ) - .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") - .emit(); - } - ty::FnPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using function pointers as const generic parameters is forbidden", - ) - .emit(); - } - ty::RawPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using raw pointers as const generic parameters is forbidden", - ) - .emit(); - } - _ => { - let mut diag = struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - non_structural_match_ty, - ); - - if ty == non_structural_match_ty { - diag.span_label( - hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); - } - - diag.emit(); - } - } - } + enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { + let trait_def_id = + tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)); + wfcx.register_bound( + ObligationCause::new( + hir_ty.span, + param.def_id, + ObligationCauseCode::WellFormed(Some(hir_ty.span)), + ), + wfcx.param_env, + ty, + trait_def_id, + ); + }); } else { let err_ty_str; let mut is_ptr = true; diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 87ae30619c63b..a6f792ed0e3e9 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,3 +1,5 @@ +use crate::marker::ConstParamTy; + /// Are values of a type transmutable into values of another type? /// /// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of @@ -33,6 +35,9 @@ pub struct Assume { pub validity: bool, } +#[unstable(feature = "transmutability", issue = "99571")] +impl ConstParamTy for Assume {} + impl Assume { /// Do not assume that *you* have ensured any safety properties are met. #[unstable(feature = "transmutability", issue = "99571")] From 8ab10bacdf6ea55c594252062f80b7e931d93d7c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 17 May 2023 18:51:45 +0000 Subject: [PATCH 595/806] remove search_for_adt_const_param_violation --- .../rustc_trait_selection/src/traits/mod.rs | 4 +- .../src/traits/structural_match.rs | 70 +++++-------------- 2 files changed, 19 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index f7389bda159e5..c2f94cb638566 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -62,9 +62,7 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{ specialization_graph, translate_substs, translate_substs_with_cause, OverlapError, }; -pub use self::structural_match::{ - search_for_adt_const_param_violation, search_for_structural_match_violation, -}; +pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index e38ae9381c1d7..420f8c5dceb50 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -34,24 +34,7 @@ pub fn search_for_structural_match_violation<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) - .break_value() -} - -/// This method traverses the structure of `ty`, trying to find any -/// types that are not allowed to be used in a const generic. -/// -/// This is either because the type does not implement `StructuralEq` -/// and `StructuralPartialEq`, or because the type is intentionally -/// not supported in const generics (such as floats and raw pointers, -/// which are allowed in match blocks). -pub fn search_for_adt_const_param_violation<'tcx>( - span: Span, - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) - .break_value() + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() } /// This implements the traversal over the structure of a given type to try to @@ -65,11 +48,6 @@ struct Search<'tcx> { /// Tracks ADTs previously encountered during search, so that /// we will not recur on them again. seen: FxHashSet, - - // Additionally deny things that have been allowed in patterns, - // but are not allowed in adt const params, such as floats and - // fn ptrs. - adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -124,41 +102,29 @@ impl<'tcx> TypeVisitor> for Search<'tcx> { } ty::FnPtr(..) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::RawPtr(..) => { - if !self.adt_const_param { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::Continue(()); } ty::Float(_) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { From a9fcb524ffbcccc0c0f56e9ecde431961a4619a9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 17 May 2023 03:00:19 +0000 Subject: [PATCH 596/806] Impl ConstParamTy for tuples, make PartialStructuralEq a supertrait too --- .../rustc_trait_selection/src/traits/misc.rs | 3 ++- library/core/src/marker.rs | 26 ++++++++++++++++--- library/core/src/tuple.rs | 25 ++++++++++++++++++ .../adt_const_params/const_param_ty_good.rs | 4 ++- .../const_param_ty_impl_no_structural_eq.rs | 2 ++ .../const_param_ty_impl_union.rs | 1 + .../const_param_ty_impl_union.stderr | 14 ++++++++-- 7 files changed, 68 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 2210ef975e6c8..e9cfd63e2eddc 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::Ref(.., hir::Mutability::Not) => return Ok(()), + | ty::Ref(.., hir::Mutability::Not) + | ty::Tuple(_) => return Ok(()), &ty::Adt(adt, substs) => (adt, substs), diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 2d2d5d4917588..9a541ccaeacbc 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -205,6 +205,20 @@ pub trait StructuralPartialEq { // Empty. } +marker_impls! { + #[unstable(feature = "structural_match", issue = "31434")] + StructuralPartialEq for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + bool, + char, + str /* Technically requires `[u8]: StructuralEq` */, + (), + {T, const N: usize} [T; N], + {T} [T], + {T: ?Sized} &T, +} + /// Required trait for constants used in pattern matches. /// /// Any type that derives `Eq` automatically implements this trait, *regardless* @@ -267,6 +281,7 @@ marker_impls! { bool, char, str /* Technically requires `[u8]: StructuralEq` */, + (), {T, const N: usize} [T; N], {T} [T], {T: ?Sized} &T, @@ -974,7 +989,8 @@ pub trait PointerLike {} #[lang = "const_param_ty"] #[unstable(feature = "adt_const_params", issue = "95174")] #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] -pub trait ConstParamTy: StructuralEq {} +#[allow(multiple_supertrait_upcastable)] +pub trait ConstParamTy: StructuralEq + StructuralPartialEq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] @@ -983,8 +999,7 @@ pub macro ConstParamTy($item:item) { /* compiler built-in */ } -// FIXME(generic_const_parameter_types): handle `ty::FnDef`/`ty::Closure` -// FIXME(generic_const_parameter_types): handle `ty::Tuple` +// FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] ConstParamTy for @@ -998,6 +1013,11 @@ marker_impls! { {T: ?Sized + ConstParamTy} &T, } +// FIXME(adt_const_params): Add to marker_impls call above once not in bootstrap +#[unstable(feature = "adt_const_params", issue = "95174")] +#[cfg(not(bootstrap))] +impl ConstParamTy for () {} + /// A common trait implemented by all function pointers. #[unstable( feature = "fn_ptr_trait", diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index a1388dfeee643..ac8d04a82860e 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,6 +1,9 @@ // See src/libstd/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; +#[cfg(not(bootstrap))] +use crate::marker::ConstParamTy; +use crate::marker::{StructuralEq, StructuralPartialEq}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -45,6 +48,28 @@ macro_rules! tuple_impls { {} } + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + #[cfg(not(bootstrap))] + impl<$($T: ConstParamTy),+> ConstParamTy for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralPartialEq for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralEq for ($($T,)+) + {} + } + maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs index 87ae83dd96603..100ab332a40d8 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs @@ -49,5 +49,7 @@ fn main() { check::>(); check::>(); - // FIXME: test tuples + check::<()>(); + check::<(i32,)>(); + check::<(D, D)>(); } diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index 37986de481f11..08f7c5cb5423e 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -9,9 +9,11 @@ struct CantParam(ImplementsConstParamTy); impl std::marker::ConstParamTy for CantParam {} //~^ error: the type `CantParam` does not `#[derive(Eq)]` +//~| error: the type `CantParam` does not `#[derive(PartialEq)]` #[derive(std::marker::ConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(Eq)]` +//~| error: the type `CantParamDerive` does not `#[derive(PartialEq)]` struct CantParamDerive(ImplementsConstParamTy); fn check() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs index d70377a20c170..c04e96c569b41 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs @@ -14,6 +14,7 @@ impl Eq for Union {} impl std::marker::StructuralEq for Union {} impl std::marker::ConstParamTy for Union {} +//~^ ERROR the type `Union` does not `#[derive(PartialEq)]` #[derive(std::marker::ConstParamTy)] //~^ ERROR this trait cannot be derived for unions diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr index 2937030460503..985b933c40c79 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr @@ -1,8 +1,18 @@ error: this trait cannot be derived for unions - --> $DIR/const_param_ty_impl_union.rs:18:10 + --> $DIR/const_param_ty_impl_union.rs:19:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0277]: the type `Union` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_union.rs:16:36 + | +LL | impl std::marker::ConstParamTy for Union {} + | ^^^^^ the trait `StructuralPartialEq` is not implemented for `Union` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0277`. From 847d50453c3e78399b3af193762673a220c23562 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 17 May 2023 04:05:46 +0000 Subject: [PATCH 597/806] Implement custom diagnostic for ConstParamTy --- .../src/error_codes/E0741.md | 12 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 3 + .../src/traits/error_reporting/mod.rs | 110 ++++++++++++++++++ .../src/traits/error_reporting/suggestions.rs | 3 +- .../ui/same_functions_in_if_condition.rs | 4 +- .../ui/same_functions_in_if_condition.stderr | 24 ++-- .../const-generics/hash-tyvid-regression-1.rs | 14 ++- .../const-generics/hash-tyvid-regression-2.rs | 16 ++- .../issue-77708-3.rs | 13 ++- .../const_param_ty_bad.stderr | 20 ++++ ...onst_param_ty_impl_no_structural_eq.stderr | 23 +++- ...const-param-with-additional-obligations.rs | 17 +++ ...t-param-with-additional-obligations.stderr | 11 ++ .../float-generic.adt_const_params.stderr | 2 - .../forbid-non-structural_match-types.rs | 6 +- .../forbid-non-structural_match-types.stderr | 12 +- ...y-size-in-generic-struct-param.full.stderr | 2 +- ...ay-size-in-generic-struct-param.min.stderr | 4 +- .../array-size-in-generic-struct-param.rs | 4 + tests/ui/const-generics/invalid-enum.rs | 4 +- tests/ui/const-generics/invalid-enum.stderr | 14 +-- tests/ui/const-generics/issue-66451.rs | 6 +- tests/ui/const-generics/issue-66451.stderr | 2 +- tests/ui/const-generics/issue-80471.rs | 2 +- tests/ui/const-generics/issue-80471.stderr | 8 +- .../issues/issue-63322-forbid-dyn.full.stderr | 4 +- .../issues/issue-63322-forbid-dyn.rs | 2 +- .../issues/issue-71381.full.stderr | 17 +-- tests/ui/const-generics/issues/issue-71381.rs | 10 +- .../issues/issue-71611.full.stderr | 11 +- tests/ui/const-generics/issues/issue-71611.rs | 4 +- .../issues/issue-74255.min.stderr | 2 +- tests/ui/const-generics/issues/issue-74255.rs | 4 + .../issues/issue-74950.min.stderr | 10 +- tests/ui/const-generics/issues/issue-74950.rs | 3 + tests/ui/const-generics/issues/issue-87076.rs | 4 +- tests/ui/const-generics/issues/issue-97278.rs | 2 +- .../const-generics/issues/issue-97278.stderr | 8 +- tests/ui/const-generics/issues/issue-99641.rs | 4 +- .../const-generics/issues/issue-99641.stderr | 8 +- tests/ui/const-generics/overlapping_impls.rs | 5 +- .../std/const-generics-range.full.stderr | 39 +++++++ .../std/const-generics-range.min.stderr | 12 +- .../std/const-generics-range.rs | 3 +- .../consts/refs_check_const_eq-issue-88384.rs | 4 +- .../refs_check_const_eq-issue-88384.stderr | 16 ++- tests/ui/mir/thir-constparam-temp.rs | 4 +- tests/ui/mir/thir-constparam-temp.stderr | 6 +- .../const-generics-structural-demangling.rs | 25 ++-- ...onst-generics-structural-demangling.stderr | 62 +++++----- 51 files changed, 455 insertions(+), 152 deletions(-) create mode 100644 tests/ui/const-generics/const-param-with-additional-obligations.rs create mode 100644 tests/ui/const-generics/const-param-with-additional-obligations.stderr create mode 100644 tests/ui/const-generics/std/const-generics-range.full.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0741.md b/compiler/rustc_error_codes/src/error_codes/E0741.md index 70d963cd41f21..0c7010526655e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0741.md +++ b/compiler/rustc_error_codes/src/error_codes/E0741.md @@ -10,15 +10,19 @@ struct A; struct B; // error! ``` -Only structural-match types (that is, types that derive `PartialEq` and `Eq`) -may be used as the types of const generic parameters. +Only structural-match types, which are types that derive `PartialEq` and `Eq` +and implement `ConstParamTy`, may be used as the types of const generic +parameters. -To fix the previous code example, we derive `PartialEq` and `Eq`: +To fix the previous code example, we derive `PartialEq`, `Eq`, and +`ConstParamTy`: ``` #![feature(adt_const_params)] -#[derive(PartialEq, Eq)] // We derive both traits here. +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] // We derive both traits here. struct A; struct B; // ok! diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5a5a405d9e7a8..69e32c35ed8d3 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -836,7 +836,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { ObligationCause::new( hir_ty.span, param.def_id, - ObligationCauseCode::WellFormed(Some(hir_ty.span)), + ObligationCauseCode::ConstParam(ty), ), wfcx.param_env, ty, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a903a76974da..bf3872e81d4a7 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -445,6 +445,9 @@ pub enum ObligationCauseCode<'tcx> { /// Obligations to prove that a `std::ops::Drop` impl is not stronger than /// the ADT it's being implemented for. DropImpl, + + /// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy` + ConstParam(Ty<'tcx>), } /// The 'location' at which we try to perform HIR-based wf checking. 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 1470dc452a18d..01c74be7057c9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -149,6 +149,12 @@ pub trait TypeErrCtxtExt<'tcx> { root_obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>, ); + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -641,6 +647,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span = obligation.cause.span; } } + if let ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, @@ -657,6 +664,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } + // Report a const-param specific error + if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives() + { + self.report_const_param_not_wf(ty, &obligation).emit(); + return; + } + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { @@ -1163,6 +1177,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.point_at_returns_when_relevant(&mut err, &obligation); err.emit(); } + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let span = obligation.cause.span; + + let mut diag = match ty.kind() { + _ if ty.has_param() => { + span_bug!(span, "const param tys cannot mention other generic parameters"); + } + ty::Float(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + } + ty::FnPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + } + ty::RawPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + } + ty::Adt(def, _) => { + // We should probably see if we're *allowed* to derive `ConstParamTy` on the type... + let mut diag = struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter", + ); + // Only suggest derive if this isn't a derived obligation, + // and the struct is local. + if let Some(span) = self.tcx.hir().span_if_local(def.did()) + && obligation.cause.code().parent().is_none() + { + if ty.is_structural_eq_shallow(self.tcx) { + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy)]` to the struct", + "#[derive(ConstParamTy)]\n", + Applicability::MachineApplicable, + ); + } else { + // FIXME(adt_const_params): We should check there's not already an + // overlapping `Eq`/`PartialEq` impl. + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + "#[derive(ConstParamTy, PartialEq, Eq)]\n", + Applicability::MachineApplicable, + ); + } + } + diag + } + _ => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` can't be used as a const parameter type", + ) + } + }; + + let mut code = obligation.cause.code(); + let mut pred = obligation.predicate.to_opt_poly_trait_pred(); + while let Some((next_code, next_pred)) = code.parent() { + if let Some(pred) = pred { + let pred = self.instantiate_binder_with_placeholders(pred); + diag.note(format!( + "`{}` must implement `{}`, but it does not", + pred.self_ty(), + pred.print_modifiers_and_trait_path() + )); + } + code = next_code; + pred = next_pred; + } + + diag + } } trait InferCtxtPrivExt<'tcx> { 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 b5b8c7fe3ac56..204d6fd043b3c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2655,7 +2655,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) | ObligationCauseCode::RustCall - | ObligationCauseCode::DropImpl => {} + | ObligationCauseCode::DropImpl + | ObligationCauseCode::ConstParam(_) => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index aea1507cc5bd9..08916398cbb28 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -10,6 +10,8 @@ clippy::uninlined_format_args )] +use std::marker::ConstParamTy; + fn function() -> bool { true } @@ -96,7 +98,7 @@ fn main() { }; println!("{}", os); - #[derive(PartialEq, Eq)] + #[derive(PartialEq, Eq, ConstParamTy)] enum E { A, B, diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index aade3b1fa4571..6aacc73b90dc1 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,11 +1,11 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:37:15 + --> $DIR/same_functions_in_if_condition.rs:39:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:36:8 + --> $DIR/same_functions_in_if_condition.rs:38:8 | LL | if function() { | ^^^^^^^^^^ @@ -16,61 +16,61 @@ LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:42:15 + --> $DIR/same_functions_in_if_condition.rs:44:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:41:8 + --> $DIR/same_functions_in_if_condition.rs:43:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:47:15 + --> $DIR/same_functions_in_if_condition.rs:49:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:46:8 + --> $DIR/same_functions_in_if_condition.rs:48:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:52:15 + --> $DIR/same_functions_in_if_condition.rs:54:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:51:8 + --> $DIR/same_functions_in_if_condition.rs:53:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:59:15 + --> $DIR/same_functions_in_if_condition.rs:61:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:57:8 + --> $DIR/same_functions_in_if_condition.rs:59:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:64:15 + --> $DIR/same_functions_in_if_condition.rs:66:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:62:8 + --> $DIR/same_functions_in_if_condition.rs:64:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/incremental/const-generics/hash-tyvid-regression-1.rs b/tests/incremental/const-generics/hash-tyvid-regression-1.rs index 5ff7b19d8945e..06d674234510a 100644 --- a/tests/incremental/const-generics/hash-tyvid-regression-1.rs +++ b/tests/incremental/const-generics/hash-tyvid-regression-1.rs @@ -1,8 +1,20 @@ // revisions: cfail #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} + // regression test for #77650 -fn c() +fn c() where [T; N.get()]: Sized, { diff --git a/tests/incremental/const-generics/hash-tyvid-regression-2.rs b/tests/incremental/const-generics/hash-tyvid-regression-2.rs index 5cdd43cd782c4..33f226ff611e8 100644 --- a/tests/incremental/const-generics/hash-tyvid-regression-2.rs +++ b/tests/incremental/const-generics/hash-tyvid-regression-2.rs @@ -1,11 +1,23 @@ // revisions: cfail #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} + // regression test for #77650 -struct C([T; N.get()]) +struct C([T; N.get()]) where [T; N.get()]: Sized; -impl<'a, const N: core::num::NonZeroUsize, A, B: PartialEq> PartialEq<&'a [A]> for C +impl<'a, const N: NonZeroUsize, A, B: PartialEq> PartialEq<&'a [A]> for C where [B; N.get()]: Sized, { diff --git a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs index fc114f224a2fa..f1c108fed11ba 100644 --- a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs +++ b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs @@ -2,7 +2,18 @@ #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] -use std::{convert::TryFrom, num::NonZeroUsize}; +use std::{convert::TryFrom}; + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} struct A([u8; N.get()]) where diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr index de5704ee429ed..48910b82a1032 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr @@ -11,6 +11,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this function + | +LL | check(main()); + | ++ error[E0277]: `[closure@$DIR/const_param_ty_bad.rs:8:11: 8:13]` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:8:11 @@ -25,6 +29,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this closure + | +LL | check(|| {}()); + | ++ error[E0277]: `fn()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:9:11 @@ -39,6 +47,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this function pointer + | +LL | check(main as fn()()); + | ++ error[E0277]: `&mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:10:11 @@ -48,11 +60,17 @@ LL | check(&mut ()); | | | required by a bound introduced by this call | + = note: `ConstParamTy` is implemented for `&()`, but not for `&mut ()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: consider removing the leading `&`-reference + | +LL - check(&mut ()); +LL + check(()); + | error[E0277]: `*mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:11:11 @@ -62,6 +80,7 @@ LL | check(&mut () as *mut ()); | | | required by a bound introduced by this call | + = help: the trait `ConstParamTy` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | @@ -76,6 +95,7 @@ LL | check(&() as *const ()); | | | required by a bound introduced by this call | + = help: the trait `ConstParamTy` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index 52701d5591442..43c5b96dc7cb8 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -1,3 +1,12 @@ +error[E0277]: the type `CantParam` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 + | +LL | impl std::marker::ConstParamTy for CantParam {} + | ^^^^^^^^^ the trait `StructuralPartialEq` is not implemented for `CantParam` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + error[E0277]: the type `CantParam` does not `#[derive(Eq)]` --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 | @@ -7,8 +16,18 @@ LL | impl std::marker::ConstParamTy for CantParam {} note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL +error[E0277]: the type `CantParamDerive` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 + | +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralPartialEq` is not implemented for `CantParamDerive` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0277]: the type `CantParamDerive` does not `#[derive(Eq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:13:10 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralEq` is not implemented for `CantParamDerive` @@ -17,6 +36,6 @@ note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.rs b/tests/ui/const-generics/const-param-with-additional-obligations.rs new file mode 100644 index 0000000000000..f53cf85cdd39c --- /dev/null +++ b/tests/ui/const-generics/const-param-with-additional-obligations.rs @@ -0,0 +1,17 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(Eq, PartialEq)] +struct Foo(T); + +trait Other {} + +impl ConstParamTy for Foo where T: Other + ConstParamTy {} + +fn foo>() {} +//~^ ERROR `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter +//~| NOTE `u8` must implement `Other`, but it does not + +fn main() {} diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.stderr b/tests/ui/const-generics/const-param-with-additional-obligations.stderr new file mode 100644 index 0000000000000..f7ec4d57401c1 --- /dev/null +++ b/tests/ui/const-generics/const-param-with-additional-obligations.stderr @@ -0,0 +1,11 @@ +error[E0741]: `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-param-with-additional-obligations.rs:13:17 + | +LL | fn foo>() {} + | ^^^^^^^ + | + = note: `u8` must implement `Other`, but it does not + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/const-generics/float-generic.adt_const_params.stderr b/tests/ui/const-generics/float-generic.adt_const_params.stderr index fef5ef0d1facf..6fe5390471ddc 100644 --- a/tests/ui/const-generics/float-generic.adt_const_params.stderr +++ b/tests/ui/const-generics/float-generic.adt_const_params.stderr @@ -3,8 +3,6 @@ error[E0741]: `f32` is forbidden as the type of a const generic parameter | LL | fn foo() {} | ^^^ - | - = note: floats do not derive `Eq` or `Ord`, which are required for const parameters error: aborting due to previous error diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.rs b/tests/ui/const-generics/forbid-non-structural_match-types.rs index 6ae9d5cfbb54f..4fec2a9f32f81 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.rs +++ b/tests/ui/const-generics/forbid-non-structural_match-types.rs @@ -1,13 +1,15 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] struct A; struct B; // ok struct C; -struct D; //~ ERROR `C` must be annotated with `#[derive(PartialEq, Eq)]` +struct D; //~ ERROR `C` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.stderr b/tests/ui/const-generics/forbid-non-structural_match-types.stderr index 81b9bdfbd6027..0efb9e9d3c287 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/tests/ui/const-generics/forbid-non-structural_match-types.stderr @@ -1,8 +1,14 @@ -error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/forbid-non-structural_match-types.rs:11:19 +error[E0741]: `C` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/forbid-non-structural_match-types.rs:13:19 | LL | struct D; - | ^ `C` doesn't derive both `PartialEq` and `Eq` + | ^ + | +help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct + | +LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL | struct C; + | error: aborting due to previous error diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr index 1d10dfdf10c6e..c478718b4cc79 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr @@ -7,7 +7,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = help: try adding a `where` bound using this expression: `where [(); 0 + N]:` error: overly complex generic constant - --> $DIR/array-size-in-generic-struct-param.rs:19:15 + --> $DIR/array-size-in-generic-struct-param.rs:23:15 | LL | arr: [u8; CFG.arr_size], | ^^^^^^^^^^^^ field access is not supported in generic constants diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr index 18e9135d07235..956e9c9c988a4 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr @@ -8,7 +8,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/array-size-in-generic-struct-param.rs:19:15 + --> $DIR/array-size-in-generic-struct-param.rs:23:15 | LL | arr: [u8; CFG.arr_size], | ^^^ cannot perform const operation using `CFG` @@ -17,7 +17,7 @@ LL | arr: [u8; CFG.arr_size], = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: `Config` is forbidden as the type of a const generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:17:21 + --> $DIR/array-size-in-generic-struct-param.rs:21:21 | LL | struct B { | ^^^^^^ diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs index 7d3fe413c1706..33ca6dcb3049c 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs @@ -9,7 +9,11 @@ struct ArithArrayLen([u32; 0 + N]); //[full]~^ ERROR unconstrained generic constant //[min]~^^ ERROR generic parameters may not be used in const operations +#[cfg(full)] +use std::marker::ConstParamTy; + #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] struct Config { arr_size: usize, } diff --git a/tests/ui/const-generics/invalid-enum.rs b/tests/ui/const-generics/invalid-enum.rs index cb6d05349db97..fcfad300c44e5 100644 --- a/tests/ui/const-generics/invalid-enum.rs +++ b/tests/ui/const-generics/invalid-enum.rs @@ -1,7 +1,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] enum CompileFlag { A, B, diff --git a/tests/ui/const-generics/invalid-enum.stderr b/tests/ui/const-generics/invalid-enum.stderr index 0d3643f6f899a..7e8a632b34f79 100644 --- a/tests/ui/const-generics/invalid-enum.stderr +++ b/tests/ui/const-generics/invalid-enum.stderr @@ -1,5 +1,5 @@ error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:21:12 + --> $DIR/invalid-enum.rs:23:12 | LL | test_1::(); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | test_1::(); | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:25:15 + --> $DIR/invalid-enum.rs:27:15 | LL | test_2::<_, CompileFlag::A>(0); | ^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | test_2::<_, CompileFlag::A>(0); | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:29:18 + --> $DIR/invalid-enum.rs:31:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _: Example = Example { x: 0 }; | help: try using the variant's enum: `CompileFlag` error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:29:18 + --> $DIR/invalid-enum.rs:31:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; | + + error[E0747]: type provided when a constant was expected - --> $DIR/invalid-enum.rs:33:18 + --> $DIR/invalid-enum.rs:35:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:21:12 + --> $DIR/invalid-enum.rs:23:12 | LL | test_1::(); | ^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | test_1::<{ CompileFlag::A }>(); | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:25:15 + --> $DIR/invalid-enum.rs:27:15 | LL | test_2::<_, CompileFlag::A>(0); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/issue-66451.rs b/tests/ui/const-generics/issue-66451.rs index 3335f7d598480..c8d5515e98739 100644 --- a/tests/ui/const-generics/issue-66451.rs +++ b/tests/ui/const-generics/issue-66451.rs @@ -1,13 +1,15 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(Debug, PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Foo { value: i32, nested: &'static Bar, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Bar(T); struct Test; diff --git a/tests/ui/const-generics/issue-66451.stderr b/tests/ui/const-generics/issue-66451.stderr index e0cb0b661ff65..946d5148667ec 100644 --- a/tests/ui/const-generics/issue-66451.stderr +++ b/tests/ui/const-generics/issue-66451.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-66451.rs:27:10 + --> $DIR/issue-66451.rs:29:10 | LL | let y: Test<{ | ____________- diff --git a/tests/ui/const-generics/issue-80471.rs b/tests/ui/const-generics/issue-80471.rs index d0af8a5eaa89f..fa6f1fde435a0 100644 --- a/tests/ui/const-generics/issue-80471.rs +++ b/tests/ui/const-generics/issue-80471.rs @@ -8,6 +8,6 @@ enum Nat { } fn foo() {} -//~^ ERROR `Box` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +//~^ ERROR `Nat` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/issue-80471.stderr b/tests/ui/const-generics/issue-80471.stderr index b89706710bcf2..3b7143de543d7 100644 --- a/tests/ui/const-generics/issue-80471.stderr +++ b/tests/ui/const-generics/issue-80471.stderr @@ -7,11 +7,17 @@ LL | #![feature(adt_const_params)] = note: see issue #95174 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0741]: `Box` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `Nat` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/issue-80471.rs:10:17 | LL | fn foo() {} | ^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | enum Nat { + | error: aborting due to previous error; 1 warning emitted diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr index 16fabd1e88fd0..e2d8c5ca0e148 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -1,8 +1,10 @@ -error[E0741]: `(dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `&'static (dyn A + 'static)` can't be used as a const parameter type --> $DIR/issue-63322-forbid-dyn.rs:9:18 | LL | fn test() { | ^^^^^^^^^^^^^^ + | + = note: `(dyn A + 'static)` must implement `ConstParamTy`, but it does not error: aborting due to previous error diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs index 116c3fcfb2170..8bc35ab3d3795 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -7,7 +7,7 @@ struct B; impl A for B {} fn test() { - //[full]~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used + //[full]~^ ERROR `&'static (dyn A + 'static)` can't be used as a const parameter type //[min]~^^ ERROR `&'static (dyn A + 'static)` is forbidden unimplemented!() } diff --git a/tests/ui/const-generics/issues/issue-71381.full.stderr b/tests/ui/const-generics/issues/issue-71381.full.stderr index 962eaf75b98f5..b6460e0017fa5 100644 --- a/tests/ui/const-generics/issues/issue-71381.full.stderr +++ b/tests/ui/const-generics/issues/issue-71381.full.stderr @@ -14,19 +14,6 @@ LL | const FN: unsafe extern "C" fn(Args), | = note: type parameters may not be used in the type of const parameters -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:14:61 - | -LL | pub fn call_me(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:23:19 - | -LL | const FN: unsafe extern "C" fn(Args), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0741, E0770. -For more information about an error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71381.rs b/tests/ui/const-generics/issues/issue-71381.rs index 66f819dbe06e2..8a878efb42a0e 100644 --- a/tests/ui/const-generics/issues/issue-71381.rs +++ b/tests/ui/const-generics/issues/issue-71381.rs @@ -12,8 +12,8 @@ unsafe extern "C" fn pass(args: PassArg) { impl Test { pub fn call_me(&self) { - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters self.0 = Self::trampiline:: as _ } @@ -21,8 +21,8 @@ impl Test { Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args), - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters >( args: Args, ) { @@ -31,6 +31,6 @@ impl Test { } fn main() { - let x = Test(); + let x = Test(std::ptr::null()); x.call_me::() } diff --git a/tests/ui/const-generics/issues/issue-71611.full.stderr b/tests/ui/const-generics/issues/issue-71611.full.stderr index e109459f2be6f..b55f410a02370 100644 --- a/tests/ui/const-generics/issues/issue-71611.full.stderr +++ b/tests/ui/const-generics/issues/issue-71611.full.stderr @@ -6,13 +6,6 @@ LL | fn func(outer: A) { | = note: type parameters may not be used in the type of const parameters -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:5:21 - | -LL | fn func(outer: A) { - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0741, E0770. -For more information about an error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71611.rs b/tests/ui/const-generics/issues/issue-71611.rs index fbb91ca18aa4f..c917f66818bad 100644 --- a/tests/ui/const-generics/issues/issue-71611.rs +++ b/tests/ui/const-generics/issues/issue-71611.rs @@ -3,8 +3,8 @@ #![cfg_attr(full, allow(incomplete_features))] fn func(outer: A) { - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters F(outer); } diff --git a/tests/ui/const-generics/issues/issue-74255.min.stderr b/tests/ui/const-generics/issues/issue-74255.min.stderr index b462d84487ee8..bbcf8682b7119 100644 --- a/tests/ui/const-generics/issues/issue-74255.min.stderr +++ b/tests/ui/const-generics/issues/issue-74255.min.stderr @@ -1,5 +1,5 @@ error: `IceEnum` is forbidden as the type of a const generic parameter - --> $DIR/issue-74255.rs:14:31 + --> $DIR/issue-74255.rs:18:31 | LL | fn ice_struct_fn() {} | ^^^^^^^ diff --git a/tests/ui/const-generics/issues/issue-74255.rs b/tests/ui/const-generics/issues/issue-74255.rs index 0e523926fb0b1..60b2fd37c44f6 100644 --- a/tests/ui/const-generics/issues/issue-74255.rs +++ b/tests/ui/const-generics/issues/issue-74255.rs @@ -3,7 +3,11 @@ #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] +#[cfg(full)] +use std::marker::ConstParamTy; + #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] enum IceEnum { Variant } diff --git a/tests/ui/const-generics/issues/issue-74950.min.stderr b/tests/ui/const-generics/issues/issue-74950.min.stderr index 729ecc2022c99..c37ee93d420fd 100644 --- a/tests/ui/const-generics/issues/issue-74950.min.stderr +++ b/tests/ui/const-generics/issues/issue-74950.min.stderr @@ -1,5 +1,5 @@ error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -8,7 +8,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -17,7 +17,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -26,7 +26,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -35,7 +35,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ diff --git a/tests/ui/const-generics/issues/issue-74950.rs b/tests/ui/const-generics/issues/issue-74950.rs index 3e1ca4735db61..43bb322656b3e 100644 --- a/tests/ui/const-generics/issues/issue-74950.rs +++ b/tests/ui/const-generics/issues/issue-74950.rs @@ -3,8 +3,11 @@ #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] +#[cfg(full)] +use std::marker::ConstParamTy; #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] struct Inner; // Note: We emit the error 5 times if we don't deduplicate: diff --git a/tests/ui/const-generics/issues/issue-87076.rs b/tests/ui/const-generics/issues/issue-87076.rs index 8a567678b824e..a32c1f965f8b5 100644 --- a/tests/ui/const-generics/issues/issue-87076.rs +++ b/tests/ui/const-generics/issues/issue-87076.rs @@ -3,7 +3,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] pub struct UnitDims { pub time: u8, pub length: u8, diff --git a/tests/ui/const-generics/issues/issue-97278.rs b/tests/ui/const-generics/issues/issue-97278.rs index da0a9776fd40f..8e7a1fcd99597 100644 --- a/tests/ui/const-generics/issues/issue-97278.rs +++ b/tests/ui/const-generics/issues/issue-97278.rs @@ -9,6 +9,6 @@ enum Bar { } fn test() {} -//~^ ERROR `Arc` must be annotated with `#[derive(PartialEq, Eq)]` +//~^ ERROR `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/issues/issue-97278.stderr b/tests/ui/const-generics/issues/issue-97278.stderr index ff13cb505ab9b..31e92f840e189 100644 --- a/tests/ui/const-generics/issues/issue-97278.stderr +++ b/tests/ui/const-generics/issues/issue-97278.stderr @@ -1,8 +1,14 @@ -error[E0741]: `Arc` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/issue-97278.rs:11:20 | LL | fn test() {} | ^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | enum Bar { + | error: aborting due to previous error diff --git a/tests/ui/const-generics/issues/issue-99641.rs b/tests/ui/const-generics/issues/issue-99641.rs index fae6d3fc41fb8..dd075a6ad0524 100644 --- a/tests/ui/const-generics/issues/issue-99641.rs +++ b/tests/ui/const-generics/issues/issue-99641.rs @@ -3,10 +3,10 @@ fn main() { pub struct Color; - //~^ ERROR using function pointers + //~^ ERROR `(fn(),)` can't be used as a const parameter type impl Color { - //~^ ERROR using function pointers + //~^ ERROR `(fn(),)` can't be used as a const parameter type pub fn new() -> Self { Color:: } diff --git a/tests/ui/const-generics/issues/issue-99641.stderr b/tests/ui/const-generics/issues/issue-99641.stderr index 349ebba08d53f..800aec3ef2ce7 100644 --- a/tests/ui/const-generics/issues/issue-99641.stderr +++ b/tests/ui/const-generics/issues/issue-99641.stderr @@ -1,14 +1,18 @@ -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `(fn(),)` can't be used as a const parameter type --> $DIR/issue-99641.rs:5:35 | LL | pub struct Color; | ^^^^^^^ + | + = note: `fn()` must implement `ConstParamTy`, but it does not -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `(fn(),)` can't be used as a const parameter type --> $DIR/issue-99641.rs:8:23 | LL | impl Color { | ^^^^^^^ + | + = note: `fn()` must implement `ConstParamTy`, but it does not error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/overlapping_impls.rs b/tests/ui/const-generics/overlapping_impls.rs index e599eadd8cf48..2ce6c4a823c37 100644 --- a/tests/ui/const-generics/overlapping_impls.rs +++ b/tests/ui/const-generics/overlapping_impls.rs @@ -2,7 +2,8 @@ #![allow(incomplete_features)] #![feature(adt_const_params)] #![feature(generic_const_exprs)] -use std::marker::PhantomData; + +use std::marker::{ConstParamTy, PhantomData}; struct Foo {} @@ -22,7 +23,7 @@ pub struct Foo2 { _marker: PhantomData, } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, ConstParamTy)] pub enum Protocol { Variant1, Variant2, diff --git a/tests/ui/const-generics/std/const-generics-range.full.stderr b/tests/ui/const-generics/std/const-generics-range.full.stderr new file mode 100644 index 0000000000000..5bf48ad738587 --- /dev/null +++ b/tests/ui/const-generics/std/const-generics-range.full.stderr @@ -0,0 +1,39 @@ +error[E0741]: `std::ops::Range` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:8:24 + | +LL | struct _Range>; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeFrom` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:13:28 + | +LL | struct _RangeFrom>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeFull` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:18:28 + | +LL | struct _RangeFull; + | ^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:24:33 + | +LL | struct _RangeInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeTo` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:29:26 + | +LL | struct _RangeTo>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeToInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:34:35 + | +LL | struct _RangeToInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index d4b2ad6fd0c7b..53fca6e884a9a 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -1,5 +1,5 @@ error: `std::ops::Range` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:7:24 + --> $DIR/const-generics-range.rs:8:24 | LL | struct _Range>; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | struct _Range>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeFrom` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:12:28 + --> $DIR/const-generics-range.rs:13:28 | LL | struct _RangeFrom>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | struct _RangeFrom>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeFull` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:17:28 + --> $DIR/const-generics-range.rs:18:28 | LL | struct _RangeFull; | ^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | struct _RangeFull; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:23:33 + --> $DIR/const-generics-range.rs:24:33 | LL | struct _RangeInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | struct _RangeInclusive>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeTo` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:28:26 + --> $DIR/const-generics-range.rs:29:26 | LL | struct _RangeTo>; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | struct _RangeTo>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeToInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:33:35 + --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/std/const-generics-range.rs b/tests/ui/const-generics/std/const-generics-range.rs index 46c06f312b98f..bda59f3ec4590 100644 --- a/tests/ui/const-generics/std/const-generics-range.rs +++ b/tests/ui/const-generics/std/const-generics-range.rs @@ -1,4 +1,5 @@ -// [full] check-pass +// [full] known-bug: unknown + // revisions: full min #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.rs b/tests/ui/consts/refs_check_const_eq-issue-88384.rs index 1496b28bd3ee6..fb0405b651cc5 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.rs +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.rs @@ -8,10 +8,10 @@ struct CompileTimeSettings{ } struct Foo; -//~^ ERROR using function pointers as const generic parameters is forbidden +//~^ ERROR `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter impl Foo { - //~^ ERROR using function pointers as const generic parameters is forbidden + //~^ ERROR `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter fn call_hooks(){ } } diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr index 3855b5f2a68e0..c490cd053e70e 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -7,17 +7,29 @@ LL | #![feature(adt_const_params)] = note: see issue #95174 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/refs_check_const_eq-issue-88384.rs:10:21 | LL | struct Foo; | ^^^^^^^^^^^^^^^^^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings{ + | -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/refs_check_const_eq-issue-88384.rs:13:15 | LL | impl Foo { | ^^^^^^^^^^^^^^^^^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings{ + | error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/mir/thir-constparam-temp.rs b/tests/ui/mir/thir-constparam-temp.rs index cdc5910b36cfa..7eedc325d60f6 100644 --- a/tests/ui/mir/thir-constparam-temp.rs +++ b/tests/ui/mir/thir-constparam-temp.rs @@ -3,7 +3,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] struct Yikes; impl Yikes { diff --git a/tests/ui/mir/thir-constparam-temp.stderr b/tests/ui/mir/thir-constparam-temp.stderr index b77d67e084f6c..d50747e5434fa 100644 --- a/tests/ui/mir/thir-constparam-temp.stderr +++ b/tests/ui/mir/thir-constparam-temp.stderr @@ -1,5 +1,5 @@ warning: taking a mutable reference to a `const` item - --> $DIR/thir-constparam-temp.rs:14:5 + --> $DIR/thir-constparam-temp.rs:16:5 | LL | YIKES.mut_self() | ^^^^^^^^^^^^^^^^ @@ -7,12 +7,12 @@ LL | YIKES.mut_self() = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $DIR/thir-constparam-temp.rs:10:5 + --> $DIR/thir-constparam-temp.rs:12:5 | LL | fn mut_self(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/thir-constparam-temp.rs:13:8 + --> $DIR/thir-constparam-temp.rs:15:8 | LL | fn foo() { | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.rs b/tests/ui/symbol-names/const-generics-structural-demangling.rs index df09ba494a74f..947fddf3f31b5 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.rs +++ b/tests/ui/symbol-names/const-generics-structural-demangling.rs @@ -1,14 +1,13 @@ // build-fail // compile-flags: -C symbol-mangling-version=v0 --crate-name=c -// NOTE(eddyb) we need `core` for `core::option::Option`, normalize away its -// disambiguator hash, which can/should change (including between stage{1,2}). -// normalize-stderr-test: "core\[[0-9a-f]+\]" -> "core[HASH]" // normalize-stderr-test: "c\[[0-9a-f]+\]" -> "c[HASH]" #![feature(adt_const_params, decl_macro, rustc_attrs)] #![allow(incomplete_features)] +use std::marker::ConstParamTy; + pub struct RefByte; #[rustc_symbol_name] @@ -43,25 +42,31 @@ pub struct TupleByteBool; //~| ERROR demangling-alt(>) impl TupleByteBool<{(1, false)}> {} -pub struct OptionUsize>; +#[derive(PartialEq, Eq, ConstParamTy)] +pub enum MyOption { + Some(T), + None, +} + +pub struct OptionUsize>; // HACK(eddyb) the full mangling is only in `.stderr` because we can normalize // the `core` disambiguator hash away there, but not here. #[rustc_symbol_name] //~^ ERROR symbol-name //~| ERROR demangling -//~| ERROR demangling-alt(::None}>>) -impl OptionUsize<{None}> {} +//~| ERROR demangling-alt(::None}>>) +impl OptionUsize<{MyOption::None}> {} // HACK(eddyb) the full mangling is only in `.stderr` because we can normalize // the `core` disambiguator hash away there, but not here. #[rustc_symbol_name] //~^ ERROR symbol-name //~| ERROR demangling -//~| ERROR demangling-alt(::Some(0)}>>) -impl OptionUsize<{Some(0)}> {} +//~| ERROR demangling-alt(::Some(0)}>>) +impl OptionUsize<{MyOption::Some(0)}> {} -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, ConstParamTy)] pub struct Foo { s: &'static str, ch: char, @@ -78,7 +83,7 @@ impl Foo_<{Foo { s: "abc", ch: 'x', slice: &[1, 2, 3] }}> {} // NOTE(eddyb) this tests specifically the use of disambiguators in field names, // using macros 2.0 hygiene to create a `struct` with conflicting field names. macro duplicate_field_name_test($x:ident) { - #[derive(PartialEq, Eq)] + #[derive(PartialEq, Eq, ConstParamTy)] pub struct Bar { $x: u8, x: u16, diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.stderr b/tests/ui/symbol-names/const-generics-structural-demangling.stderr index a4c997477ee79..96dea154d05c0 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.stderr +++ b/tests/ui/symbol-names/const-generics-structural-demangling.stderr @@ -1,131 +1,131 @@ error: symbol-name(_RMCsCRATE_HASH_1cINtB_7RefByteKRh7b_E) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs_CsCRATE_HASH_1cINtB_6RefZstKRAEE) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs0_CsCRATE_HASH_1cINtB_11Array3BytesKAh1_h2_h3_EE) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs1_CsCRATE_HASH_1cINtB_13TupleByteBoolKTh1_b0_EE) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs2_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtNtCsCRATE_HASH_4core6option6OptionjE4NoneUE) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: symbol-name(_RMs2_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtB_8MyOptionjE4NoneUE) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(::None}>>) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: demangling(::None}>>) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling-alt(::None}>>) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: demangling-alt(::None}>>) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs3_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtNtCsCRATE_HASH_4core6option6OptionjE4SomeTj0_EE) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: symbol-name(_RMs3_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtB_8MyOptionjE4SomeTj0_EE) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(::Some(0usize)}>>) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: demangling(::Some(0usize)}>>) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling-alt(::Some(0)}>>) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: demangling-alt(::Some(0)}>>) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs4_CsCRATE_HASH_1cINtB_4Foo_KVNtB_3FooS1sRe616263_2chc78_5sliceRAh1_h2_h3_EEE) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs9_CsCRATE_HASH_1cINtB_4Bar_KVNtB_3BarS1xh7b_s_1xt1000_EE) - --> $DIR/const-generics-structural-demangling.rs:88:5 +error: symbol-name(_RMsf_CsCRATE_HASH_1cINtB_4Bar_KVNtB_3BarS1xh7b_s_1xt1000_EE) + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | duplicate_field_name_test!(x); = note: this error originates in the macro `duplicate_field_name_test` (in Nightly builds, run with -Z macro-backtrace for more info) error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:88:5 + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ @@ -147,7 +147,7 @@ LL | duplicate_field_name_test!(x); = note: this error originates in the macro `duplicate_field_name_test` (in Nightly builds, run with -Z macro-backtrace for more info) error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:88:5 + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ From 97bacbab57128725887c3c9600bb0ad83ca42e50 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 19 May 2023 22:04:25 +0000 Subject: [PATCH 598/806] Remove adt_const_params usage from compiler --- .../rustc_errors/src/diagnostic_builder.rs | 32 +++++-------------- compiler/rustc_errors/src/lib.rs | 3 +- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 7d9d0c76450d8..08ff2cfba5c26 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -115,36 +115,22 @@ pub trait EmissionGuarantee: Sized { ) -> DiagnosticBuilder<'_, Self>; } -/// Private module for sealing the `IsError` helper trait. -mod sealed_level_is_error { - use crate::Level; - - /// Sealed helper trait for statically checking that a `Level` is an error. - pub(crate) trait IsError {} - - impl IsError<{ Level::Bug }> for () {} - impl IsError<{ Level::DelayedBug }> for () {} - impl IsError<{ Level::Fatal }> for () {} - // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints - // don't need error guarantees, as their levels are always dynamic. - impl IsError<{ Level::Error { lint: false } }> for () {} -} - impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. #[track_caller] - pub(crate) fn new_guaranteeing_error, const L: Level>( + pub(crate) fn new_guaranteeing_error>( handler: &'a Handler, message: M, - ) -> Self - where - (): sealed_level_is_error::IsError, - { + ) -> Self { Self { inner: DiagnosticBuilderInner { state: DiagnosticBuilderState::Emittable(handler), - diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)), + diagnostic: Box::new(Diagnostic::new_with_code( + Level::Error { lint: false }, + None, + message, + )), }, _marker: PhantomData, } @@ -203,9 +189,7 @@ impl EmissionGuarantee for ErrorGuaranteed { handler: &Handler, msg: impl Into, ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>( - handler, msg, - ) + DiagnosticBuilder::new_guaranteeing_error(handler, msg) } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 6c5f3e62454ab..bf77ed81f9bd0 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -6,7 +6,6 @@ #![feature(array_windows)] #![feature(drain_filter)] #![feature(if_let_guard)] -#![feature(adt_const_params)] #![feature(let_chains)] #![feature(never_type)] #![feature(result_option_inspect)] @@ -845,7 +844,7 @@ impl Handler { &self, msg: impl Into, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg) + DiagnosticBuilder::new_guaranteeing_error(self, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. From 780719b2dcf1f1f229564f026310ca59a108d5e1 Mon Sep 17 00:00:00 2001 From: sladynnunes Date: Sun, 28 May 2023 01:25:54 -0700 Subject: [PATCH 599/806] Migrate to Askama Implemented wrap_item_write Update wrap_item --- src/librustdoc/html/render/print_item.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 62027a3fa1941..394f97930e79d 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1102,7 +1102,12 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: ); } -fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { +fn item_trait_alias( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::TraitAlias, +) { wrap_item(w, |w| { write!( w, @@ -1112,16 +1117,17 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds(&t.bounds, true, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + .unwrap(); } fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { @@ -1666,13 +1672,14 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_item(w: &mut Buffer, f: F) +fn wrap_item(w: &mut W, f: F) where - F: FnOnce(&mut Buffer), + W: fmt::Write, + F: FnOnce(&mut W), { - w.write_str(r#"

"#);
+    write!(w, r#"
"#).unwrap();
     f(w);
-    w.write_str("
"); + write!(w, "
").unwrap(); } #[derive(PartialEq, Eq)] From faf31b5052a929159f2663acee49feefcf0b2fb6 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 1 Jun 2023 18:00:19 +0000 Subject: [PATCH 600/806] CFI: Fix cfi with repr(transparent): transform_ty: unexpected Alias(Proj Fixes #111185 by normalizing ty::Alias before encoding. --- .../src/typeid/typeid_itanium_cxx_abi.rs | 13 ++++++++++--- ...zer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index b245742e53372..bf1faaa4193ee 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -674,12 +674,12 @@ fn encode_ty<'tcx>( } // Unexpected types - ty::Bound(..) + ty::Alias(..) + | ty::Bound(..) | ty::Error(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(..) - | ty::Alias(..) | ty::Placeholder(..) => { bug!("encode_ty: unexpected `{:?}`", ty.kind()); } @@ -913,12 +913,19 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ); } + ty::Alias(..) => { + ty = transform_ty( + tcx, + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), + options, + ); + } + ty::Bound(..) | ty::Error(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(..) - | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { bug!("transform_ty: unexpected `{:?}`", ty.kind()); diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs index ebb26cd35c09c..472d921ace046 100644 --- a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs @@ -587,6 +587,6 @@ pub fn foo149(_: Type14, _: Type14, _: Type14) { } // CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIvEE"} // CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIvES_E"} // CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIvES_S_E"} -// CHECK: ![[TYPE147]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarE -// CHECK: ![[TYPE148]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_E -// CHECK: ![[TYPE149]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_S_E +// CHECK: ![[TYPE147]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarE"} +// CHECK: ![[TYPE148]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_E"} +// CHECK: ![[TYPE149]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_S_E"} From 36a91abba3c16a3c7cdff2c20f5391bf30dbf5e4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 Jun 2023 22:24:52 +0000 Subject: [PATCH 601/806] Assert that closures and generators are made with the right number of substitutions --- compiler/rustc_middle/src/ty/context.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b05e791211d10..fac3d87fe1bd8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1872,18 +1872,28 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty_from_kind(Closure(closure_id, closure_substs)) + pub fn mk_closure(self, def_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { + debug_assert_eq!( + closure_substs.len(), + self.generics_of(self.typeck_root_def_id(def_id)).count() + 3, + "closure constructed with incorrect substitutions" + ); + self.mk_ty_from_kind(Closure(def_id, closure_substs)) } #[inline] pub fn mk_generator( self, - id: DefId, + def_id: DefId, generator_substs: SubstsRef<'tcx>, movability: hir::Movability, ) -> Ty<'tcx> { - self.mk_ty_from_kind(Generator(id, generator_substs, movability)) + debug_assert_eq!( + generator_substs.len(), + self.generics_of(self.typeck_root_def_id(def_id)).count() + 5, + "generator constructed with incorrect number of substitutions" + ); + self.mk_ty_from_kind(Generator(def_id, generator_substs, movability)) } #[inline] From 76ff5ec88624ea61ae49b728ce4d382df8fb7fe3 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Wed, 24 May 2023 15:44:52 +0000 Subject: [PATCH 602/806] CFI: Fix cfi with async: transform_ty: unexpected GeneratorWitness(Binde Fixes #111184 by encoding ty::Generator parent substs only. --- .../src/typeid/typeid_itanium_cxx_abi.rs | 29 +++++++++++++++---- .../issue-111184-generator-witness.rs | 17 +++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 tests/ui/sanitize/issue-111184-generator-witness.rs diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index da8a16dee8ac7..da3dafedd9b24 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -608,9 +608,7 @@ fn encode_ty<'tcx>( } // Function types - ty::FnDef(def_id, substs) - | ty::Closure(def_id, substs) - | ty::Generator(def_id, substs, ..) => { + ty::FnDef(def_id, substs) | ty::Closure(def_id, substs) => { // u[IE], where is , // as vendor extended type. let mut s = String::new(); @@ -621,6 +619,23 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + ty::Generator(def_id, substs, ..) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + // Encode parent substs only + s.push_str(&encode_substs( + tcx, + tcx.mk_substs(substs.as_generator().parent_substs()), + dict, + options, + )); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + // Pointer types ty::Ref(region, ty0, ..) => { // [U3mut]u3refIE as vendor extended type qualifier and type @@ -739,7 +754,12 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio let mut ty = ty; match ty.kind() { - ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) => {} + ty::Float(..) + | ty::Char + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::GeneratorWitness(..) => {} ty::Bool => { if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { @@ -922,7 +942,6 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty::Bound(..) | ty::Error(..) - | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(..) | ty::Alias(..) diff --git a/tests/ui/sanitize/issue-111184-generator-witness.rs b/tests/ui/sanitize/issue-111184-generator-witness.rs new file mode 100644 index 0000000000000..8f4118057cec4 --- /dev/null +++ b/tests/ui/sanitize/issue-111184-generator-witness.rs @@ -0,0 +1,17 @@ +// Regression test for issue 111184, where ty::GeneratorWitness were not expected to occur in +// encode_ty and caused the compiler to ICE. +// +// needs-sanitizer-cfi +// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021 +// no-prefer-dynamic +// only-x86_64-unknown-linux-gnu +// run-pass + +use std::future::Future; + +async fn foo() {} +fn bar(_: impl Future) {} + +fn main() { + bar(foo()); +} From 6f13a37499191a19f5115a8cbe8b5431f269ab7e Mon Sep 17 00:00:00 2001 From: beetrees Date: Sat, 8 Apr 2023 14:53:50 +0100 Subject: [PATCH 603/806] Add spans to `clippy.toml` error messages --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/lib.rs | 39 ++-- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/utils/conf.rs | 207 ++++++++++-------- lintcheck/Cargo.toml | 2 +- src/driver.rs | 15 +- tests/ui-toml/bad_toml/conf_bad_toml.stderr | 6 +- .../bad_toml_type/conf_bad_type.stderr | 6 +- .../conf_deprecated_key.stderr | 12 +- tests/ui-toml/duplicated_keys/clippy.toml | 3 - .../duplicated_keys/duplicated_keys.stderr | 12 +- .../duplicated_keys_deprecated/clippy.toml | 3 + .../duplicated_keys.rs | 1 + .../duplicated_keys.stderr | 14 ++ .../duplicated_keys_deprecated_2/clippy.toml | 4 + .../duplicated_keys.rs | 1 + .../duplicated_keys.stderr | 14 ++ tests/ui-toml/toml_unknown_key/clippy.toml | 4 +- .../toml_unknown_key/conf_unknown_key.stderr | 70 +++++- 20 files changed, 278 insertions(+), 141 deletions(-) create mode 100644 tests/ui-toml/duplicated_keys_deprecated/clippy.toml create mode 100644 tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs create mode 100644 tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr create mode 100644 tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml create mode 100644 tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs create mode 100644 tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr diff --git a/Cargo.toml b/Cargo.toml index 3c72bb62ed19e..8739e45808084 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ termize = "0.1" compiletest_rs = { version = "0.10", features = ["tmp"] } tester = "0.9" regex = "1.5" -toml = "0.5" +toml = "0.7.3" walkdir = "2.3" # This is used by the `collect-metadata` alias. filetime = "0.2" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index a0db69b652db0..3419d8c7872cd 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -21,7 +21,7 @@ regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } tempfile = { version = "3.3.0", optional = true } -toml = "0.5" +toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 38f4ecc75b6fb..b728a445c2076 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -334,7 +334,7 @@ mod zero_sized_map_values; pub use crate::utils::conf::{lookup_conf_file, Conf}; use crate::utils::{ - conf::{format_error, metadata::get_configuration_metadata, TryConf}, + conf::{metadata::get_configuration_metadata, TryConf}, FindAll, }; @@ -370,23 +370,36 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option, Vec }, }; - let TryConf { conf, errors, warnings } = utils::conf::read(file_name); + let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - format_error(error) - )); + if let Some(span) = error.span { + sess.span_err( + span, + format!("error reading Clippy's configuration file: {}", error.message), + ); + } else { + sess.err(format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + error.message + )); + } } for warning in warnings { - sess.struct_warn(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - format_error(warning) - )) - .emit(); + if let Some(span) = warning.span { + sess.span_warn( + span, + format!("error reading Clippy's configuration file: {}", warning.message), + ); + } else { + sess.warn(format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + warning.message + )); + } } conf diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 6c909e5ed73ea..2d79a5c90081c 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -241,7 +241,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { V: de::MapAccess<'de>, { let mut name = None; - let mut brace: Option<&str> = None; + let mut brace: Option = None; while let Some(key) = map.next_key()? { match key { Field::Name => { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f6de66bb5145b..030894b58f7c2 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,12 +2,15 @@ #![allow(clippy::module_name_repetitions)] +use rustc_session::Session; +use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::Deserialize; -use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::Range; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::{cmp, env, fmt, fs, io, iter}; +use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ @@ -67,33 +70,70 @@ impl DisallowedPath { #[derive(Default)] pub struct TryConf { pub conf: Conf, - pub errors: Vec>, - pub warnings: Vec>, + pub errors: Vec, + pub warnings: Vec, } impl TryConf { - fn from_error(error: impl Error + 'static) -> Self { + fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { + ConfError::from_toml(file, error).into() + } +} + +impl From for TryConf { + fn from(value: ConfError) -> Self { Self { conf: Conf::default(), - errors: vec![Box::new(error)], + errors: vec![value], warnings: vec![], } } } +impl From for TryConf { + fn from(value: io::Error) -> Self { + ConfError::from(value).into() + } +} + #[derive(Debug)] -struct ConfError(String); +pub struct ConfError { + pub message: String, + pub span: Option, +} + +impl ConfError { + fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { + if let Some(span) = error.span() { + Self::spanned(file, error.message(), span) + } else { + Self { + message: error.message().to_string(), + span: None, + } + } + } -impl fmt::Display for ConfError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(&self.0, f) + fn spanned(file: &SourceFile, message: impl Into, span: Range) -> Self { + Self { + message: message.into(), + span: Some(Span::new( + file.start_pos + BytePos::from_usize(span.start), + file.start_pos + BytePos::from_usize(span.end), + SyntaxContext::root(), + None, + )), + } } } -impl Error for ConfError {} - -fn conf_error(s: impl Into) -> Box { - Box::new(ConfError(s.into())) +impl From for ConfError { + fn from(value: io::Error) -> Self { + Self { + message: value.to_string(), + span: None, + } + } } macro_rules! define_Conf { @@ -117,20 +157,14 @@ macro_rules! define_Conf { } } - impl<'de> Deserialize<'de> for TryConf { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - deserializer.deserialize_map(ConfVisitor) - } - } - #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] #[allow(non_camel_case_types)] enum Field { $($name,)* third_party, } - struct ConfVisitor; + struct ConfVisitor<'a>(&'a SourceFile); - impl<'de> Visitor<'de> for ConfVisitor { + impl<'de> Visitor<'de> for ConfVisitor<'_> { type Value = TryConf; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -141,32 +175,38 @@ macro_rules! define_Conf { let mut errors = Vec::new(); let mut warnings = Vec::new(); $(let mut $name = None;)* - // could get `Field` here directly, but get `str` first for diagnostics - while let Some(name) = map.next_key::<&str>()? { - match Field::deserialize(name.into_deserializer())? { - $(Field::$name => { - $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)? - match map.next_value() { - Err(e) => errors.push(conf_error(e.to_string())), + // could get `Field` here directly, but get `String` first for diagnostics + while let Some(name) = map.next_key::>()? { + match Field::deserialize(name.get_ref().as_str().into_deserializer()) { + Err(e) => { + let e: FieldError = e; + errors.push(ConfError::spanned(self.0, e.0, name.span())); + } + $(Ok(Field::$name) => { + $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)? + let raw_value = map.next_value::>()?; + let value_span = raw_value.span(); + match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)), Ok(value) => match $name { - Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))), + Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())), None => { $name = Some(value); // $new_conf is the same as one of the defined `$name`s, so // this variable is defined in line 2 of this function. $(match $new_conf { - Some(_) => errors.push(conf_error(concat!( + Some(_) => errors.push(ConfError::spanned(self.0, concat!( "duplicate field `", stringify!($new_conf), "` (provided as `", stringify!($name), "`)" - ))), + ), name.span())), None => $new_conf = $name.clone(), })? }, } } })* - // white-listed; ignore - Field::third_party => drop(map.next_value::()) + // ignore contents of the third_party key + Ok(Field::third_party) => drop(map.next_value::()) } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; @@ -532,19 +572,19 @@ pub fn lookup_conf_file() -> io::Result<(Option, Vec)> { /// Read the `toml` configuration file. /// /// In case of error, the function tries to continue as much as possible. -pub fn read(path: &Path) -> TryConf { - let content = match fs::read_to_string(path) { - Err(e) => return TryConf::from_error(e), - Ok(content) => content, +pub fn read(sess: &Session, path: &Path) -> TryConf { + let file = match sess.source_map().load_file(path) { + Err(e) => return e.into(), + Ok(file) => file, }; - match toml::from_str::(&content) { + match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(&file)) { Ok(mut conf) => { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); conf }, - Err(e) => TryConf::from_error(e), + Err(e) => TryConf::from_toml_error(&file, &e), } } @@ -556,65 +596,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec, default: &[&str]) { const SEPARATOR_WIDTH: usize = 4; -// Check whether the error is "unknown field" and, if so, list the available fields sorted and at -// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it. -pub fn format_error(error: Box) -> String { - let s = error.to_string(); - - if_chain! { - if error.downcast::().is_ok(); - if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s); - then { - use fmt::Write; - - fields.sort_unstable(); - - let (rows, column_widths) = calculate_dimensions(&fields); - - let mut msg = String::from(prefix); - for row in 0..rows { - writeln!(msg).unwrap(); - for (column, column_width) in column_widths.iter().copied().enumerate() { - let index = column * rows + row; - let field = fields.get(index).copied().unwrap_or_default(); - write!( - msg, - "{:SEPARATOR_WIDTH$}{field:column_width$}", - " " - ) - .unwrap(); - } - } - write!(msg, "\n{suffix}").unwrap(); - msg - } else { - s - } +#[derive(Debug)] +struct FieldError(String); + +impl std::error::Error for FieldError {} + +impl Display for FieldError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.pad(&self.0) } } -// `parse_unknown_field_message` will become unnecessary if -// https://github.com/alexcrichton/toml-rs/pull/364 is merged. -fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> { - // An "unknown field" message has the following form: - // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y - // ^^ ^^^^ ^^ - if_chain! { - if s.starts_with("unknown field"); - let slices = s.split("`, `").collect::>(); - let n = slices.len(); - if n >= 2; - if let Some((prefix, first_field)) = slices[0].rsplit_once(" `"); - if let Some((last_field, suffix)) = slices[n - 1].split_once("` "); - then { - let fields = iter::once(first_field) - .chain(slices[1..n - 1].iter().copied()) - .chain(iter::once(last_field)) - .collect::>(); - Some((prefix, fields, suffix)) - } else { - None +impl serde::de::Error for FieldError { + fn custom(msg: T) -> Self { + Self(msg.to_string()) + } + + fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { + // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is + // set and allows it. + use fmt::Write; + + let mut expected = expected.to_vec(); + expected.sort_unstable(); + + let (rows, column_widths) = calculate_dimensions(&expected); + + let mut msg = format!("unknown field `{field}`, expected one of"); + for row in 0..rows { + writeln!(msg).unwrap(); + for (column, column_width) in column_widths.iter().copied().enumerate() { + let index = column * rows + row; + let field = expected.get(index).copied().unwrap_or_default(); + write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap(); + } } + Self(msg) } } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 27d32f390035d..a828d12370467 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -22,7 +22,7 @@ rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" tar = "0.4" -toml = "0.5" +toml = "0.7.3" ureq = "2.2" walkdir = "2.3" diff --git a/src/driver.rs b/src/driver.rs index 205905d509135..90ee3ff6eb623 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -74,7 +74,7 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option) /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy /// when any of them are modified -fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { +fn track_files(parse_sess: &mut ParseSess) { let file_depinfo = parse_sess.file_depinfo.get_mut(); // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` @@ -83,10 +83,7 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { file_depinfo.insert(Symbol::intern("Cargo.toml")); } - // `clippy.toml` - if let Some(path) = conf_path_string { - file_depinfo.insert(Symbol::intern(&path)); - } + // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()` // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt @@ -130,17 +127,11 @@ impl rustc_driver::Callbacks for ClippyCallbacks { #[allow(rustc::bad_opt_access)] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_lints::lookup_conf_file(); - let conf_path_string = if let Ok((Some(path), _)) = &conf_path { - path.to_str().map(String::from) - } else { - None - }; - let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); - track_files(parse_sess, conf_path_string); + track_files(parse_sess); })); config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that diff --git a/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/tests/ui-toml/bad_toml/conf_bad_toml.stderr index 28c1a568a632b..5b7e8c0db744d 100644 --- a/tests/ui-toml/bad_toml/conf_bad_toml.stderr +++ b/tests/ui-toml/bad_toml/conf_bad_toml.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4 +error: error reading Clippy's configuration file: expected `.`, `=` + --> $DIR/clippy.toml:1:4 + | +LL | fn this_is_obviously(not: a, toml: file) { + | ^ error: aborting due to previous error diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index e3ec60192040e..386e1135df90d 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `disallowed-names` +error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence + --> $DIR/clippy.toml:1:20 + | +LL | disallowed-names = 42 + | ^^ error: aborting due to previous error diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 630bad07c5b71..123ad94dd09da 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,6 +1,14 @@ -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:2:1 + | +LL | cyclomatic-complexity-threshold = 2 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead +warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead + --> $DIR/clippy.toml:3:1 + | +LL | blacklisted-names = [ "..", "wibble" ] + | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) --> $DIR/conf_deprecated_key.rs:6:4 diff --git a/tests/ui-toml/duplicated_keys/clippy.toml b/tests/ui-toml/duplicated_keys/clippy.toml index 63a893cc6c795..55789afc1b71c 100644 --- a/tests/ui-toml/duplicated_keys/clippy.toml +++ b/tests/ui-toml/duplicated_keys/clippy.toml @@ -1,5 +1,2 @@ cognitive-complexity-threshold = 2 -# This is the deprecated name for the same key -cyclomatic-complexity-threshold = 3 -# Check we get duplication warning regardless of order cognitive-complexity-threshold = 4 diff --git a/tests/ui-toml/duplicated_keys/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys/duplicated_keys.stderr index d99490a242d4f..54997735274e4 100644 --- a/tests/ui-toml/duplicated_keys/duplicated_keys.stderr +++ b/tests/ui-toml/duplicated_keys/duplicated_keys.stderr @@ -1,8 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) +error: error reading Clippy's configuration file: duplicate key `cognitive-complexity-threshold` in document root + --> $DIR/clippy.toml:2:1 + | +LL | cognitive-complexity-threshold = 4 + | ^ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive-complexity-threshold` - -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead - -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to previous error diff --git a/tests/ui-toml/duplicated_keys_deprecated/clippy.toml b/tests/ui-toml/duplicated_keys_deprecated/clippy.toml new file mode 100644 index 0000000000000..7932c43ebd217 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated/clippy.toml @@ -0,0 +1,3 @@ +cognitive-complexity-threshold = 2 +# This is the deprecated name for the same key +cyclomatic-complexity-threshold = 3 diff --git a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr new file mode 100644 index 0000000000000..2ae7848f183b5 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr @@ -0,0 +1,14 @@ +error: error reading Clippy's configuration file: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) + --> $DIR/clippy.toml:3:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:3:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml b/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml new file mode 100644 index 0000000000000..53c634b727ed5 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml @@ -0,0 +1,4 @@ +# This is the deprecated name for cognitive-complexity-threshold +cyclomatic-complexity-threshold = 3 +# Check we get duplication warning regardless of order +cognitive-complexity-threshold = 4 diff --git a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr new file mode 100644 index 0000000000000..53ad427124691 --- /dev/null +++ b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr @@ -0,0 +1,14 @@ +error: error reading Clippy's configuration file: duplicate field `cognitive-complexity-threshold` + --> $DIR/clippy.toml:4:1 + | +LL | cognitive-complexity-threshold = 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:2:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/ui-toml/toml_unknown_key/clippy.toml b/tests/ui-toml/toml_unknown_key/clippy.toml index 554b87cc50be1..b77b4580051ef 100644 --- a/tests/ui-toml/toml_unknown_key/clippy.toml +++ b/tests/ui-toml/toml_unknown_key/clippy.toml @@ -1,6 +1,8 @@ # that one is an error foobar = 42 +# so is this one +barfoo = 53 -# that one is white-listed +# that one is ignored [third-party] clippy-feature = "nightly" diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 44710b09648ee..b6038f031f3c3 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of +error: error reading Clippy's configuration file: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests allow-mixed-uninlined-format-args @@ -54,7 +54,71 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports - at line 5 column 1 + --> $DIR/clippy.toml:2:1 + | +LL | foobar = 42 + | ^^^^^^ -error: aborting due to previous error +error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of + allow-dbg-in-tests + allow-expect-in-tests + allow-mixed-uninlined-format-args + allow-print-in-tests + allow-unwrap-in-tests + allowed-scripts + arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary + array-size-threshold + avoid-breaking-exported-api + await-holding-invalid-types + blacklisted-names + cargo-ignore-publish + cognitive-complexity-threshold + cyclomatic-complexity-threshold + disallowed-macros + disallowed-methods + disallowed-names + disallowed-types + doc-valid-idents + enable-raw-pointer-heuristic-for-send + enforced-import-renames + enum-variant-name-threshold + enum-variant-size-threshold + future-size-threshold + ignore-interior-mutability + large-error-threshold + literal-representation-threshold + matches-for-let-else + max-fn-params-bools + max-include-file-size + max-struct-bools + max-suggested-slice-pattern-length + max-trait-bounds + missing-docs-in-crate-items + msrv + pass-by-value-size-limit + semicolon-inside-block-ignore-singleline + semicolon-outside-block-ignore-multiline + single-char-binding-names-threshold + standard-macro-braces + suppress-restriction-lint-in-const + third-party + too-large-for-stack + too-many-arguments-threshold + too-many-lines-threshold + trivial-copy-size-limit + type-complexity-threshold + unnecessary-box-size + unreadable-literal-lint-fractions + upper-case-acronyms-aggressive + vec-box-size-threshold + verbose-bit-mask-threshold + warn-on-all-wildcard-imports + --> $DIR/clippy.toml:4:1 + | +LL | barfoo = 53 + | ^^^^^^ + +error: aborting due to 2 previous errors From c625880a416ebea68c956590694cd13bbae53148 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 10 Feb 2023 10:26:24 +1100 Subject: [PATCH 604/806] Improve comments on `TyCtxt` and `GlobalCtxt`. --- compiler/rustc_middle/src/ty/context.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b05e791211d10..673d09cddf4ab 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -478,6 +478,17 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { /// [rustc dev guide] for more details. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html +/// +/// An implementation detail: `TyCtxt` is a wrapper type for [GlobalCtxt], +/// which is the struct that actually holds all the data. `TyCtxt` derefs to +/// `GlobalCtxt`, and in practice `TyCtxt` is passed around everywhere, and all +/// operations are done via `TyCtxt`. A `TyCtxt` is obtained for a `GlobalCtxt` +/// by calling `enter` with a closure `f`. That function creates both the +/// `TyCtxt`, and an `ImplicitCtxt` around it that is put into TLS. Within `f`: +/// - The `ImplicitCtxt` is available implicitly via TLS. +/// - The `TyCtxt` is available explicitly via the `tcx` parameter, and also +/// implicitly within the `ImplicitCtxt`. Explicit access is preferred when +/// possible. #[derive(Copy, Clone)] #[rustc_diagnostic_item = "TyCtxt"] #[rustc_pass_by_value] @@ -493,6 +504,7 @@ impl<'tcx> Deref for TyCtxt<'tcx> { } } +/// See [TyCtxt] for details about this type. pub struct GlobalCtxt<'tcx> { pub arena: &'tcx WorkerLocal>, pub hir_arena: &'tcx WorkerLocal>, From 731601ccd1617a9ca4b1fe09fdb5906c2019110b Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 2 Jun 2023 00:41:02 +0000 Subject: [PATCH 605/806] Check tuple elements are `Sized` in `offset_of` --- compiler/rustc_hir_typeck/src/expr.rs | 19 ++++++++++--------- tests/ui/offset-of/offset-of-dst-field.rs | 2 ++ tests/ui/offset-of/offset-of-dst-field.stderr | 19 ++++++++++++++----- tests/ui/offset-of/offset-of-unsized.rs | 3 ++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 19ff77d8349aa..5e10add013b05 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3117,16 +3117,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } ty::Tuple(tys) => { - let fstr = field.as_str(); - - if let Ok(index) = fstr.parse::() { - if fstr == index.to_string() { - if let Some(&field_ty) = tys.get(index) { - field_indices.push(index.into()); - current_container = field_ty; + if let Ok(index) = field.as_str().parse::() + && field.name == sym::integer(index) + { + for ty in tys.iter().take(index + 1) { + self.require_type_is_sized(ty, expr.span, traits::MiscObligation); + } + if let Some(&field_ty) = tys.get(index) { + field_indices.push(index.into()); + current_container = field_ty; - continue; - } + continue; } } } diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index 3b8dc0b84a460..e393b159e64d5 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -36,6 +36,8 @@ fn main() { offset_of!(Alpha, z); //~ ERROR the size for values of type offset_of!(Beta, z); //~ ERROR the size for values of type offset_of!(Gamma, z); //~ ERROR the size for values of type + offset_of!((u8, dyn Trait), 0); // ok + offset_of!((u8, dyn Trait), 1); //~ ERROR the size for values of type } fn delta() { diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index 128c783d5dd2d..4eaceaa935817 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -25,8 +25,17 @@ LL | offset_of!(Gamma, z); = help: the trait `Sized` is not implemented for `Extern` = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:40:5 + | +LL | offset_of!((u8, dyn Trait), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Trait` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0277]: the size for values of type `Extern` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:43:5 + --> $DIR/offset-of-dst-field.rs:45:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -35,7 +44,7 @@ LL | offset_of!(Delta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:44:5 + --> $DIR/offset-of-dst-field.rs:46:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -44,7 +53,7 @@ LL | offset_of!(Delta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:42:5 + --> $DIR/offset-of-dst-field.rs:44:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -58,7 +67,7 @@ LL | struct Alpha { = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:48:5 + --> $DIR/offset-of-dst-field.rs:50:5 | LL | fn generic_with_maybe_sized() -> usize { | - this type parameter needs to be `std::marker::Sized` @@ -72,6 +81,6 @@ LL - fn generic_with_maybe_sized() -> usize { LL + fn generic_with_maybe_sized() -> usize { | -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-unsized.rs b/tests/ui/offset-of/offset-of-unsized.rs index 666387e615ef2..49c8328da5c55 100644 --- a/tests/ui/offset-of/offset-of-unsized.rs +++ b/tests/ui/offset-of/offset-of-unsized.rs @@ -1,5 +1,6 @@ // build-pass -// regression test for #112051 +// regression test for #112051, not in `offset-of-dst` as the issue is in codegen, +// and isn't triggered in the presence of typeck errors #![feature(offset_of)] From d722f27684f30e5ada53309608b5ca308ca2e0ce Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 2 Jun 2023 01:29:39 +0000 Subject: [PATCH 606/806] Test invalid tuple field identifiers --- tests/ui/offset-of/offset-of-tuple.rs | 10 ++++++ tests/ui/offset-of/offset-of-tuple.stderr | 37 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 tests/ui/offset-of/offset-of-tuple.rs create mode 100644 tests/ui/offset-of/offset-of-tuple.stderr diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs new file mode 100644 index 0000000000000..4077538b77f27 --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -0,0 +1,10 @@ +#![feature(offset_of)] +#![feature(builtin_syntax)] + +fn main() { + core::mem::offset_of!((u8, u8), _0); //~ ERROR no field `_0` + core::mem::offset_of!((u8, u8), +1); //~ ERROR no rules expected + core::mem::offset_of!((u8, u8), -1); //~ ERROR no rules expected + builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` + builtin # offset_of((u8, u8), +1); //~ ERROR expected identifier +} diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr new file mode 100644 index 0000000000000..cc9ce0f34550c --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -0,0 +1,37 @@ +error: expected identifier, found `+` + --> $DIR/offset-of-tuple.rs:9:35 + | +LL | builtin # offset_of((u8, u8), +1); + | ^ expected identifier + +error: no rules expected the token `1` + --> $DIR/offset-of-tuple.rs:6:38 + | +LL | core::mem::offset_of!((u8, u8), +1); + | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: no rules expected the token `1` + --> $DIR/offset-of-tuple.rs:7:38 + | +LL | core::mem::offset_of!((u8, u8), -1); + | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:5:37 + | +LL | core::mem::offset_of!((u8, u8), _0); + | ^^ + +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:8:35 + | +LL | builtin # offset_of((u8, u8), _0); + | ^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0609`. From bbfadf067c893c0cbcb86ff9be7e8182220da042 Mon Sep 17 00:00:00 2001 From: cui fliter Date: Thu, 1 Jun 2023 19:44:49 +0800 Subject: [PATCH 607/806] Fix broken link Signed-off-by: cui fliter --- compiler/rustc_target/src/abi/call/x86_64.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index 9427f27d1b7bb..74ef53915c929 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -1,5 +1,5 @@ // The classification code for the x86_64 ABI is taken from the clay language -// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp +// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind}; use crate::abi::{self, Abi, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; From adb37d4999cbb83bd670bb767b3c8d08e43e3c7c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Jun 2023 18:52:11 -0700 Subject: [PATCH 608/806] Clarify when MIR `Div`/`Rem` trigger UB --- compiler/rustc_middle/src/mir/syntax.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 3e474c1d377e6..94a1e9ced772d 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1272,13 +1272,18 @@ pub enum BinOp { Mul, /// The `/` operator (division) /// - /// Division by zero is UB, because the compiler should have inserted checks - /// prior to this. + /// For integer types, division by zero is UB, as is `MIN / -1` for signed. + /// The compiler should have inserted checks prior to this. + /// + /// Floating-point division by zero is safe, and does not need guards. Div, /// The `%` operator (modulus) /// - /// Using zero as the modulus (second operand) is UB, because the compiler - /// should have inserted checks prior to this. + /// For integer types, using zero as the modulus (second operand) is UB, + /// as is `MIN % -1` for signed. + /// The compiler should have inserted checks prior to this. + /// + /// Floating-point remainder by zero is safe, and does not need guards. Rem, /// The `^` operator (bitwise xor) BitXor, From de2911f45464d595a0fabeedd83338da819ade16 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Jun 2023 11:23:21 +1000 Subject: [PATCH 609/806] Overhaul CGU formation terminology. Currently, the code uses multiple words to describe when a mono item `f` uses a mono item `g`, all of which have problems. - `f` references `g`: confusing because there are multiple kinds of use, e.g. "`f` calls `g`" is one, but "`f` takes a (`&T`-style) reference of `g`" is another, and that's two subtly different meanings of "reference" in play. - `f` accesses `g`: meh, "accesses" makes me think of data, and this is code. - `g` is a neighbor (or neighbour) of `f`: is verbose, and doesn't capture the directionality. This commit changes the code to use "`f` uses `g`" everywhere. I think it's better than the current terminology, and the consistency is important. Also, `InliningMap` is renamed `UsageMap` because (a) it was always mostly about usage, and (b) the inlining information it did record was removed in a recent commit. --- compiler/rustc_monomorphize/src/collector.rs | 171 +++++++++--------- .../rustc_monomorphize/src/partitioning.rs | 64 +++---- 2 files changed, 115 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 09025c1ee3319..345d2e3d90641 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -35,15 +35,15 @@ //! //! - A "mono item" is something that results in a function or global in //! the LLVM IR of a codegen unit. Mono items do not stand on their -//! own, they can reference other mono items. For example, if function +//! own, they can use other mono items. For example, if function //! `foo()` calls function `bar()` then the mono item for `foo()` -//! references the mono item for function `bar()`. In general, the -//! definition for mono item A referencing a mono item B is that -//! the LLVM artifact produced for A references the LLVM artifact produced +//! uses the mono item for function `bar()`. In general, the +//! definition for mono item A using a mono item B is that +//! the LLVM artifact produced for A uses the LLVM artifact produced //! for B. //! -//! - Mono items and the references between them form a directed graph, -//! where the mono items are the nodes and references form the edges. +//! - Mono items and the uses between them form a directed graph, +//! where the mono items are the nodes and uses form the edges. //! Let's call this graph the "mono item graph". //! //! - The mono item graph for a program contains all mono items @@ -53,12 +53,11 @@ //! mono item graph for the current crate. It runs in two phases: //! //! 1. Discover the roots of the graph by traversing the HIR of the crate. -//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR +//! 2. Starting from the roots, find uses by inspecting the MIR //! representation of the item corresponding to a given node, until no more //! new nodes are found. //! //! ### Discovering roots -//! //! The roots of the mono item graph correspond to the public non-generic //! syntactic items in the source code. We find them by walking the HIR of the //! crate, and whenever we hit upon a public function, method, or static item, @@ -69,25 +68,23 @@ //! specified. Functions marked `#[no_mangle]` and functions called by inlinable //! functions also always act as roots.) //! -//! ### Finding neighbor nodes -//! Given a mono item node, we can discover neighbors by inspecting its -//! MIR. We walk the MIR and any time we hit upon something that signifies a -//! reference to another mono item, we have found a neighbor. Since the -//! mono item we are currently at is always monomorphic, we also know the -//! concrete type arguments of its neighbors, and so all neighbors again will be -//! monomorphic. The specific forms a reference to a neighboring node can take -//! in MIR are quite diverse. Here is an overview: +//! ### Finding uses +//! Given a mono item node, we can discover uses by inspecting its MIR. We walk +//! the MIR to find other mono items used by each mono item. Since the mono +//! item we are currently at is always monomorphic, we also know the concrete +//! type arguments of its used mono items. The specific forms a use can take in +//! MIR are quite diverse. Here is an overview: //! //! #### Calling Functions/Methods -//! The most obvious form of one mono item referencing another is a +//! The most obvious way for one mono item to use another is a //! function or method call (represented by a CALL terminator in MIR). But -//! calls are not the only thing that might introduce a reference between two +//! calls are not the only thing that might introduce a use between two //! function mono items, and as we will see below, they are just a //! specialization of the form described next, and consequently will not get any //! special treatment in the algorithm. //! //! #### Taking a reference to a function or method -//! A function does not need to actually be called in order to be a neighbor of +//! A function does not need to actually be called in order to be used by //! another function. It suffices to just take a reference in order to introduce //! an edge. Consider the following example: //! @@ -109,18 +106,18 @@ //! The MIR of none of these functions will contain an explicit call to //! `print_val::`. Nonetheless, in order to mono this program, we need //! an instance of this function. Thus, whenever we encounter a function or -//! method in operand position, we treat it as a neighbor of the current +//! method in operand position, we treat it as a use of the current //! mono item. Calls are just a special case of that. //! //! #### Drop glue //! Drop glue mono items are introduced by MIR drop-statements. The -//! generated mono item will again have drop-glue item neighbors if the +//! generated mono item will have additional drop-glue item uses if the //! type to be dropped contains nested values that also need to be dropped. It -//! might also have a function item neighbor for the explicit `Drop::drop` +//! might also have a function item use for the explicit `Drop::drop` //! implementation of its type. //! //! #### Unsizing Casts -//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! A subtle way of introducing use edges is by casting to a trait object. //! Since the resulting fat-pointer contains a reference to a vtable, we need to //! instantiate all object-safe methods of the trait, as we need to store //! pointers to these functions even if they never get called anywhere. This can @@ -151,7 +148,7 @@ //! Mono item collection can be performed in one of two modes: //! //! - Lazy mode means that items will only be instantiated when actually -//! referenced. The goal is to produce the least amount of machine code +//! used. The goal is to produce the least amount of machine code //! possible. //! //! - Eager mode is meant to be used in conjunction with incremental compilation @@ -211,66 +208,65 @@ pub enum MonoItemCollectionMode { Lazy, } -/// Maps every mono item to all mono items it references in its -/// body. -pub struct InliningMap<'tcx> { - // Maps a source mono item to the range of mono items - // accessed by it. - // The range selects elements within the `targets` vecs. - index: FxHashMap, Range>, - targets: Vec>, +pub struct UsageMap<'tcx> { + // Maps every mono item to the mono items used by it. Those mono items + // are represented as a range, which indexes into `used_items`. + used_map: FxHashMap, Range>, + + // A mono item that is used by N different other mono items will appear + // here N times. Indexed into by the ranges in `used_map`. + used_items: Vec>, } type MonoItems<'tcx> = Vec>>; -impl<'tcx> InliningMap<'tcx> { - fn new() -> InliningMap<'tcx> { - InliningMap { index: FxHashMap::default(), targets: Vec::new() } +impl<'tcx> UsageMap<'tcx> { + fn new() -> UsageMap<'tcx> { + UsageMap { used_map: FxHashMap::default(), used_items: Vec::new() } } - fn record_accesses<'a>( + fn record_used<'a>( &mut self, - source: MonoItem<'tcx>, - new_targets: &'a [Spanned>], + user_item: MonoItem<'tcx>, + used_items: &'a [Spanned>], ) where 'tcx: 'a, { - let start_index = self.targets.len(); - let new_items_count = new_targets.len(); + let old_len = self.used_items.len(); + let new_len = old_len + used_items.len(); + let new_items_range = old_len..new_len; - self.targets.reserve(new_items_count); + self.used_items.reserve(used_items.len()); - for Spanned { node: mono_item, .. } in new_targets.into_iter() { - self.targets.push(*mono_item); + for Spanned { node: used_item, .. } in used_items.into_iter() { + self.used_items.push(*used_item); } - let end_index = self.targets.len(); - assert!(self.index.insert(source, start_index..end_index).is_none()); + assert!(self.used_map.insert(user_item, new_items_range).is_none()); } - /// Internally iterate over all items referenced by `source` which will be - /// made available for inlining. - pub fn with_inlining_candidates(&self, tcx: TyCtxt<'tcx>, source: MonoItem<'tcx>, mut f: F) + /// Internally iterate over all inlined items used by `item`. + pub fn for_each_inlined_used_item(&self, tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, mut f: F) where F: FnMut(MonoItem<'tcx>), { - if let Some(range) = self.index.get(&source) { - for candidate in self.targets[range.clone()].iter() { - let is_inlined = candidate.instantiation_mode(tcx) == InstantiationMode::LocalCopy; + if let Some(range) = self.used_map.get(&item) { + for used_item in self.used_items[range.clone()].iter() { + let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy; if is_inlined { - f(*candidate); + f(*used_item); } } } } - /// Internally iterate over all items and the things each accesses. - pub fn iter_accesses(&self, mut f: F) + /// Internally iterate over each item and the items used by it. + pub fn for_each_item_and_its_used_items(&self, mut f: F) where F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), { - for (&accessor, range) in &self.index { - f(accessor, &self.targets[range.clone()]) + for (&item, range) in &self.used_map { + f(item, &self.used_items[range.clone()]) } } } @@ -279,7 +275,7 @@ impl<'tcx> InliningMap<'tcx> { pub fn collect_crate_mono_items( tcx: TyCtxt<'_>, mode: MonoItemCollectionMode, -) -> (FxHashSet>, InliningMap<'_>) { +) -> (FxHashSet>, UsageMap<'_>) { let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); let roots = @@ -288,12 +284,12 @@ pub fn collect_crate_mono_items( debug!("building mono item graph, beginning at roots"); let mut visited = MTLock::new(FxHashSet::default()); - let mut inlining_map = MTLock::new(InliningMap::new()); + let mut usage_map = MTLock::new(UsageMap::new()); let recursion_limit = tcx.recursion_limit(); { let visited: MTLockRef<'_, _> = &mut visited; - let inlining_map: MTLockRef<'_, _> = &mut inlining_map; + let usage_map: MTLockRef<'_, _> = &mut usage_map; tcx.sess.time("monomorphization_collector_graph_walk", || { par_for_each_in(roots, |root| { @@ -304,13 +300,13 @@ pub fn collect_crate_mono_items( visited, &mut recursion_depths, recursion_limit, - inlining_map, + usage_map, ); }); }); } - (visited.into_inner(), inlining_map.into_inner()) + (visited.into_inner(), usage_map.into_inner()) } // Find all non-generic items by walking the HIR. These items serve as roots to @@ -353,24 +349,23 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec( tcx: TyCtxt<'tcx>, - starting_point: Spanned>, + starting_item: Spanned>, visited: MTLockRef<'_, FxHashSet>>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, - inlining_map: MTLockRef<'_, InliningMap<'tcx>>, + usage_map: MTLockRef<'_, UsageMap<'tcx>>, ) { - if !visited.lock_mut().insert(starting_point.node) { + if !visited.lock_mut().insert(starting_item.node) { // We've been here already, no need to search again. return; } - let mut neighbors = Vec::new(); + let mut used_items = Vec::new(); let recursion_depth_reset; - // // Post-monomorphization errors MVP // // We can encounter errors while monomorphizing an item, but we don't have a good way of @@ -396,7 +391,7 @@ fn collect_items_rec<'tcx>( // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do. let error_count = tcx.sess.diagnostic().err_count(); - match starting_point.node { + match starting_item.node { MonoItem::Static(def_id) => { let instance = Instance::mono(tcx, def_id); @@ -404,19 +399,19 @@ fn collect_items_rec<'tcx>( debug_assert!(should_codegen_locally(tcx, &instance)); let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); + visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); recursion_depth_reset = None; if let Ok(alloc) = tcx.eval_static_initializer(def_id) { for &id in alloc.inner().provenance().ptrs().values() { - collect_miri(tcx, id, &mut neighbors); + collect_miri(tcx, id, &mut used_items); } } if tcx.needs_thread_local_shim(def_id) { - neighbors.push(respan( - starting_point.span, + used_items.push(respan( + starting_item.span, MonoItem::Fn(Instance { def: InstanceDef::ThreadLocalShim(def_id), substs: InternalSubsts::empty(), @@ -432,14 +427,14 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = Some(check_recursion_limit( tcx, instance, - starting_point.span, + starting_item.span, recursion_depths, recursion_limit, )); check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_neighbours(tcx, instance, &mut neighbors); + collect_used_items(tcx, instance, &mut used_items); }); } MonoItem::GlobalAsm(item_id) => { @@ -457,13 +452,13 @@ fn collect_items_rec<'tcx>( hir::InlineAsmOperand::SymFn { anon_const } => { let fn_ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); - visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors); + visit_fn_use(tcx, fn_ty, false, *op_sp, &mut used_items); } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); if should_codegen_locally(tcx, &instance) { trace!("collecting static {:?}", def_id); - neighbors.push(dummy_spanned(MonoItem::Static(*def_id))); + used_items.push(dummy_spanned(MonoItem::Static(*def_id))); } } hir::InlineAsmOperand::In { .. } @@ -483,19 +478,19 @@ fn collect_items_rec<'tcx>( // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. if tcx.sess.diagnostic().err_count() > error_count - && starting_point.node.is_generic_fn() - && starting_point.node.is_user_defined() + && starting_item.node.is_generic_fn() + && starting_item.node.is_user_defined() { - let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string()); + let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string()); tcx.sess.emit_note(EncounteredErrorWhileInstantiating { - span: starting_point.span, + span: starting_item.span, formatted_item, }); } - inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors); + usage_map.lock_mut().record_used(starting_item.node, &used_items); - for neighbour in neighbors { - collect_items_rec(tcx, neighbour, visited, recursion_depths, recursion_limit, inlining_map); + for used_item in used_items { + collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -611,14 +606,14 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { } } -struct MirNeighborCollector<'a, 'tcx> { +struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, output: &'a mut MonoItems<'tcx>, instance: Instance<'tcx>, } -impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { +impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { pub fn monomorphize(&self, value: T) -> T where T: TypeFoldable>, @@ -632,7 +627,7 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { } } -impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { +impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { debug!("visiting rvalue {:?}", *rvalue); @@ -1392,13 +1387,13 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte /// Scans the MIR in order to find function calls, closures, and drop-glue. #[instrument(skip(tcx, output), level = "debug")] -fn collect_neighbours<'tcx>( +fn collect_used_items<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, output: &mut MonoItems<'tcx>, ) { let body = tcx.instance_mir(instance.def); - MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); + MirUsedCollector { tcx, body: &body, output, instance }.visit_body(&body); } #[instrument(skip(tcx, output), level = "debug")] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 015361f8ad5b7..5d707e62430b0 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -115,14 +115,14 @@ use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt}; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_span::symbol::Symbol; -use crate::collector::InliningMap; +use crate::collector::UsageMap; use crate::collector::{self, MonoItemCollectionMode}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, target_cgu_count: usize, - inlining_map: &'a InliningMap<'tcx>, + usage_map: &'a UsageMap<'tcx>, } struct PlacedRootMonoItems<'tcx> { @@ -138,14 +138,14 @@ fn partition<'tcx, I>( tcx: TyCtxt<'tcx>, mono_items: &mut I, max_cgu_count: usize, - inlining_map: &InliningMap<'tcx>, + usage_map: &UsageMap<'tcx>, ) -> Vec> where I: Iterator>, { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); - let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map }; + let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, usage_map }; // In the first step, we place all regular monomorphizations into their // respective 'home' codegen unit. Regular monomorphizations are all @@ -405,7 +405,7 @@ fn merge_codegen_units<'tcx>( } /// For symbol internalization, we need to know whether a symbol/mono-item is -/// accessed from outside the codegen unit it is defined in. This type is used +/// used from outside the codegen unit it is defined in. This type is used /// to keep track of that. #[derive(Clone, PartialEq, Eq, Debug)] enum MonoItemPlacement { @@ -426,7 +426,7 @@ fn place_inlined_mono_items<'tcx>( // Collect all items that need to be available in this codegen unit. let mut reachable = FxHashSet::default(); for root in old_codegen_unit.items().keys() { - follow_inlining(cx.tcx, *root, cx.inlining_map, &mut reachable); + follow_inlining(cx.tcx, *root, cx.usage_map, &mut reachable); } let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); @@ -481,16 +481,16 @@ fn place_inlined_mono_items<'tcx>( fn follow_inlining<'tcx>( tcx: TyCtxt<'tcx>, - mono_item: MonoItem<'tcx>, - inlining_map: &InliningMap<'tcx>, + item: MonoItem<'tcx>, + usage_map: &UsageMap<'tcx>, visited: &mut FxHashSet>, ) { - if !visited.insert(mono_item) { + if !visited.insert(item) { return; } - inlining_map.with_inlining_candidates(tcx, mono_item, |target| { - follow_inlining(tcx, target, inlining_map, visited); + usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { + follow_inlining(tcx, inlined_item, usage_map, visited); }); } } @@ -504,7 +504,7 @@ fn internalize_symbols<'tcx>( if codegen_units.len() == 1 { // Fast path for when there is only one codegen unit. In this case we // can internalize all candidates, since there is nowhere else they - // could be accessed from. + // could be used from. for cgu in codegen_units { for candidate in &internalization_candidates { cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default)); @@ -516,43 +516,43 @@ fn internalize_symbols<'tcx>( // Build a map from every monomorphization to all the monomorphizations that // reference it. - let mut accessor_map: FxHashMap, Vec>> = Default::default(); - cx.inlining_map.iter_accesses(|accessor, accessees| { - for accessee in accessees { - accessor_map.entry(*accessee).or_default().push(accessor); + let mut user_map: FxHashMap, Vec>> = Default::default(); + cx.usage_map.for_each_item_and_its_used_items(|user_item, used_items| { + for used_item in used_items { + user_map.entry(*used_item).or_default().push(user_item); } }); // For each internalization candidates in each codegen unit, check if it is - // accessed from outside its defining codegen unit. + // used from outside its defining codegen unit. for cgu in codegen_units { let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }; - for (accessee, linkage_and_visibility) in cgu.items_mut() { - if !internalization_candidates.contains(accessee) { + for (item, linkage_and_visibility) in cgu.items_mut() { + if !internalization_candidates.contains(item) { // This item is no candidate for internalizing, so skip it. continue; } - debug_assert_eq!(mono_item_placements[accessee], home_cgu); + debug_assert_eq!(mono_item_placements[item], home_cgu); - if let Some(accessors) = accessor_map.get(accessee) { - if accessors + if let Some(user_items) = user_map.get(item) { + if user_items .iter() - .filter_map(|accessor| { - // Some accessors might not have been + .filter_map(|user_item| { + // Some user mono items might not have been // instantiated. We can safely ignore those. - mono_item_placements.get(accessor) + mono_item_placements.get(user_item) }) .any(|placement| *placement != home_cgu) { - // Found an accessor from another CGU, so skip to the next - // item without marking this one as internal. + // Found a user from another CGU, so skip to the next item + // without marking this one as internal. continue; } } - // If we got here, we did not find any accesses from other CGUs, - // so it's fine to make this monomorphization internal. + // If we got here, we did not find any uses from other CGUs, so + // it's fine to make this monomorphization internal. *linkage_and_visibility = (Linkage::Internal, Visibility::Default); } } @@ -788,7 +788,7 @@ fn mono_item_visibility<'tcx>( } else { // If this isn't a generic function then we mark this a `Default` if // this is a reachable item, meaning that it's a symbol other crates may - // access when they link to us. + // use when they link to us. if tcx.is_reachable_non_generic(def_id.to_def_id()) { *can_be_internalized = false; debug_assert!(!is_generic); @@ -968,7 +968,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } }; - let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode); + let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); tcx.sess.abort_if_errors(); @@ -979,7 +979,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co tcx, &mut items.iter().copied(), tcx.sess.codegen_units(), - &inlining_map, + &usage_map, ); codegen_units[0].make_primary(); &*tcx.arena.alloc_from_iter(codegen_units) From 5b0c56b333a9ebf87ce183327f84d8a76c5a8524 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Jun 2023 13:00:06 +1000 Subject: [PATCH 610/806] Introduce `UsageMap::user_map`. `UsageMap` contains `used_map`, which maps from an item to the item it uses. This commit add `user_map`, which is the inverse. We already compute this inverse, but later on, and it is only held as a local variable. Its simpler and nicer to put it next to `used_map`. --- compiler/rustc_monomorphize/src/collector.rs | 24 ++++++++++--------- .../rustc_monomorphize/src/partitioning.rs | 11 +-------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 345d2e3d90641..51ccdca69c8de 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -213,6 +213,9 @@ pub struct UsageMap<'tcx> { // are represented as a range, which indexes into `used_items`. used_map: FxHashMap, Range>, + // Maps every mono item to the mono items that use it. + user_map: FxHashMap, Vec>>, + // A mono item that is used by N different other mono items will appear // here N times. Indexed into by the ranges in `used_map`. used_items: Vec>, @@ -222,7 +225,11 @@ type MonoItems<'tcx> = Vec>>; impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { - UsageMap { used_map: FxHashMap::default(), used_items: Vec::new() } + UsageMap { + used_map: FxHashMap::default(), + user_map: FxHashMap::default(), + used_items: Vec::new(), + } } fn record_used<'a>( @@ -240,11 +247,16 @@ impl<'tcx> UsageMap<'tcx> { for Spanned { node: used_item, .. } in used_items.into_iter() { self.used_items.push(*used_item); + self.user_map.entry(*used_item).or_default().push(user_item); } assert!(self.used_map.insert(user_item, new_items_range).is_none()); } + pub fn get_user_items(&self, item: MonoItem<'tcx>) -> Option<&[MonoItem<'tcx>]> { + self.user_map.get(&item).map(|items| items.as_slice()) + } + /// Internally iterate over all inlined items used by `item`. pub fn for_each_inlined_used_item(&self, tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, mut f: F) where @@ -259,16 +271,6 @@ impl<'tcx> UsageMap<'tcx> { } } } - - /// Internally iterate over each item and the items used by it. - pub fn for_each_item_and_its_used_items(&self, mut f: F) - where - F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), - { - for (&item, range) in &self.used_map { - f(item, &self.used_items[range.clone()]) - } - } } #[instrument(skip(tcx, mode), level = "debug")] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 5d707e62430b0..8932288a16173 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -514,15 +514,6 @@ fn internalize_symbols<'tcx>( return; } - // Build a map from every monomorphization to all the monomorphizations that - // reference it. - let mut user_map: FxHashMap, Vec>> = Default::default(); - cx.usage_map.for_each_item_and_its_used_items(|user_item, used_items| { - for used_item in used_items { - user_map.entry(*used_item).or_default().push(user_item); - } - }); - // For each internalization candidates in each codegen unit, check if it is // used from outside its defining codegen unit. for cgu in codegen_units { @@ -535,7 +526,7 @@ fn internalize_symbols<'tcx>( } debug_assert_eq!(mono_item_placements[item], home_cgu); - if let Some(user_items) = user_map.get(item) { + if let Some(user_items) = cx.usage_map.get_user_items(*item) { if user_items .iter() .filter_map(|user_item| { From f1fe797ee2b4f1e9d47eb468212b742960dafb87 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Jun 2023 13:25:26 +1000 Subject: [PATCH 611/806] Change representation of `UsageMap::used_map`. It currently uses ranges, which index into `UsageMap::used_items`. This commit changes it to just use `Vec`, which is much simpler to construct and use. This change does result in more allocations, but it is few enough that the perf impact is negligible. --- compiler/rustc_monomorphize/src/collector.rs | 41 ++++++-------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 51ccdca69c8de..f4ee7b7587587 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -195,7 +195,6 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_session::Limit; use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use rustc_target::abi::Size; -use std::ops::Range; use std::path::PathBuf; use crate::errors::{ @@ -209,27 +208,18 @@ pub enum MonoItemCollectionMode { } pub struct UsageMap<'tcx> { - // Maps every mono item to the mono items used by it. Those mono items - // are represented as a range, which indexes into `used_items`. - used_map: FxHashMap, Range>, + // Maps every mono item to the mono items used by it. + used_map: FxHashMap, Vec>>, // Maps every mono item to the mono items that use it. user_map: FxHashMap, Vec>>, - - // A mono item that is used by N different other mono items will appear - // here N times. Indexed into by the ranges in `used_map`. - used_items: Vec>, } type MonoItems<'tcx> = Vec>>; impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { - UsageMap { - used_map: FxHashMap::default(), - user_map: FxHashMap::default(), - used_items: Vec::new(), - } + UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } } fn record_used<'a>( @@ -239,18 +229,12 @@ impl<'tcx> UsageMap<'tcx> { ) where 'tcx: 'a, { - let old_len = self.used_items.len(); - let new_len = old_len + used_items.len(); - let new_items_range = old_len..new_len; - - self.used_items.reserve(used_items.len()); - - for Spanned { node: used_item, .. } in used_items.into_iter() { - self.used_items.push(*used_item); - self.user_map.entry(*used_item).or_default().push(user_item); + let used_items: Vec<_> = used_items.iter().map(|item| item.node).collect(); + for &used_item in used_items.iter() { + self.user_map.entry(used_item).or_default().push(user_item); } - assert!(self.used_map.insert(user_item, new_items_range).is_none()); + assert!(self.used_map.insert(user_item, used_items).is_none()); } pub fn get_user_items(&self, item: MonoItem<'tcx>) -> Option<&[MonoItem<'tcx>]> { @@ -262,12 +246,11 @@ impl<'tcx> UsageMap<'tcx> { where F: FnMut(MonoItem<'tcx>), { - if let Some(range) = self.used_map.get(&item) { - for used_item in self.used_items[range.clone()].iter() { - let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy; - if is_inlined { - f(*used_item); - } + let used_items = self.used_map.get(&item).unwrap(); + for used_item in used_items.iter() { + let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy; + if is_inlined { + f(*used_item); } } } From 3806bad6cf4f21bf9cf2085407384728bb71d673 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Jun 2023 15:44:19 +1000 Subject: [PATCH 612/806] Simplify `place_inlined_mono_items`. Currently it overwrites all the CGUs with new CGUs. But those new CGUs are just copies of the old CGUs, possibly with some things added. This commit changes things so that each CGU just gets added to in place, which makes things simpler and clearer. --- .../rustc_monomorphize/src/partitioning.rs | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 8932288a16173..d22031e25d6f1 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -422,33 +422,22 @@ fn place_inlined_mono_items<'tcx>( let single_codegen_unit = codegen_units.len() == 1; - for old_codegen_unit in codegen_units.iter_mut() { + for cgu in codegen_units.iter_mut() { // Collect all items that need to be available in this codegen unit. let mut reachable = FxHashSet::default(); - for root in old_codegen_unit.items().keys() { + for root in cgu.items().keys() { follow_inlining(cx.tcx, *root, cx.usage_map, &mut reachable); } - let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); - // Add all monomorphizations that are not already there. for mono_item in reachable { - if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { - // This is a root, just copy it over. - new_codegen_unit.items_mut().insert(mono_item, *linkage); - } else { + if !cgu.items().contains_key(&mono_item) { if roots.contains(&mono_item) { - bug!( - "GloballyShared mono-item inlined into other CGU: \ - {:?}", - mono_item - ); + bug!("GloballyShared mono-item inlined into other CGU: {:?}", mono_item); } // This is a CGU-private copy. - new_codegen_unit - .items_mut() - .insert(mono_item, (Linkage::Internal, Visibility::Default)); + cgu.items_mut().insert(mono_item, (Linkage::Internal, Visibility::Default)); } if !single_codegen_unit { @@ -458,23 +447,17 @@ fn place_inlined_mono_items<'tcx>( Entry::Occupied(e) => { let placement = e.into_mut(); debug_assert!(match *placement { - MonoItemPlacement::SingleCgu { cgu_name } => { - cgu_name != new_codegen_unit.name() - } + MonoItemPlacement::SingleCgu { cgu_name } => cgu_name != cgu.name(), MonoItemPlacement::MultipleCgus => true, }); *placement = MonoItemPlacement::MultipleCgus; } Entry::Vacant(e) => { - e.insert(MonoItemPlacement::SingleCgu { - cgu_name: new_codegen_unit.name(), - }); + e.insert(MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }); } } } } - - *old_codegen_unit = new_codegen_unit; } return mono_item_placements; From 4f800b56d02a8025308f3d16b0a49828d307f954 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Jun 2023 16:05:06 +1000 Subject: [PATCH 613/806] Clarify `follow_inlining`. I found this confusing because it includes the root item, plus the inlined items reachable from the root item. The new formulation separates the two parts more clearly. --- compiler/rustc_monomorphize/src/partitioning.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index d22031e25d6f1..79fcd62bc6206 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -426,7 +426,10 @@ fn place_inlined_mono_items<'tcx>( // Collect all items that need to be available in this codegen unit. let mut reachable = FxHashSet::default(); for root in cgu.items().keys() { - follow_inlining(cx.tcx, *root, cx.usage_map, &mut reachable); + // Insert the root item itself, plus all inlined items that are + // reachable from it without going via another root item. + reachable.insert(*root); + get_reachable_inlined_items(cx.tcx, *root, cx.usage_map, &mut reachable); } // Add all monomorphizations that are not already there. @@ -462,18 +465,17 @@ fn place_inlined_mono_items<'tcx>( return mono_item_placements; - fn follow_inlining<'tcx>( + fn get_reachable_inlined_items<'tcx>( tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, usage_map: &UsageMap<'tcx>, visited: &mut FxHashSet>, ) { - if !visited.insert(item) { - return; - } - usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { - follow_inlining(tcx, inlined_item, usage_map, visited); + let is_new = visited.insert(inlined_item); + if is_new { + get_reachable_inlined_items(tcx, inlined_item, usage_map, visited); + } }); } } From dfdc25834edb2eb31cce0b7f740656097c871662 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 2 Jun 2023 10:18:17 +0200 Subject: [PATCH 614/806] Bump nightly version -> 2023-06-02 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index bc7fb711ed8b8..0d2e1eee64384 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-05-20" +channel = "nightly-2023-06-02" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 84f8ce801eddd09d1de78b0e920edb92fde31527 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 2 Jun 2023 10:18:34 +0200 Subject: [PATCH 615/806] Bump Clippy version -> 0.1.72 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 257d067dcd84a..cd91b70d57f3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.71" +version = "0.1.72" 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/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 01cefc32a4d87..c152d6071706e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.71" +version = "0.1.72" 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/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 66a5079fa85ef..cfe686eb9b01d 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.71" +version = "0.1.72" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 139102798c42a..4dc906d00db16 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.71" +version = "0.1.72" edition = "2021" publish = false From 2f459f7f140307b5abbb7ea81440ed1843b490e7 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 2 Jun 2023 11:26:34 +0300 Subject: [PATCH 616/806] fix ptr cast --- library/std/src/sys/unix/args.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 1e4c2445232db..0efe2570d6775 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -242,13 +242,15 @@ mod imp { let mut res = Vec::new(); unsafe { - let process_info_sel = sel_registerName(c"processInfo".as_ptr()); - let arguments_sel = sel_registerName(c"arguments".as_ptr()); - let utf8_sel = sel_registerName(c"UTF8String".as_ptr()); - let count_sel = sel_registerName(c"count".as_ptr()); - let object_at_sel = sel_registerName(c"objectAtIndex:".as_ptr()); - - let klass = objc_getClass(c"NSProcessInfo".as_ptr()); + let process_info_sel = + sel_registerName(c"processInfo".as_ptr() as *const libc::c_uchar); + let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); + let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); + let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); + let object_at_sel = + sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); + + let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); let info = objc_msgSend(klass, process_info_sel); let args = objc_msgSend(info, arguments_sel); From 612c34234653ce6d56834c888298f03e69c88a4c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 2 Jun 2023 11:42:42 +0200 Subject: [PATCH 617/806] Update Cargo.lock --- Cargo.lock | 74 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7aa317ad7501..00bfbadece33c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -294,7 +294,7 @@ dependencies = [ "serde_json", "sha2", "tar", - "toml", + "toml 0.5.7", "xz2", ] @@ -311,7 +311,7 @@ dependencies = [ "indexmap", "serde", "serde_json", - "toml", + "toml 0.5.7", ] [[package]] @@ -581,7 +581,7 @@ checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "clippy" -version = "0.1.71" +version = "0.1.72" dependencies = [ "clap 4.2.1", "clippy_lints", @@ -596,7 +596,6 @@ dependencies = [ "quote", "regex", "rustc-semver", - "rustc-workspace-hack", "rustc_tools_util", "serde", "syn 2.0.8", @@ -604,7 +603,7 @@ dependencies = [ "termize", "tester", "tokio", - "toml", + "toml 0.7.4", "walkdir", ] @@ -623,7 +622,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.71" +version = "0.1.72" dependencies = [ "arrayvec", "cargo_metadata 0.15.3", @@ -639,7 +638,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml", + "toml 0.7.4", "unicode-normalization", "unicode-script", "url", @@ -647,7 +646,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.71" +version = "0.1.72" dependencies = [ "arrayvec", "if_chain", @@ -930,7 +929,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.71" +version = "0.1.72" dependencies = [ "itertools", "quote", @@ -2111,7 +2110,7 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "toml", + "toml 0.5.7", "topological-sort", ] @@ -4363,7 +4362,7 @@ dependencies = [ "serde_json", "term", "thiserror", - "toml", + "toml 0.5.7", "unicode-segmentation", "unicode-width", "unicode_categories", @@ -4484,6 +4483,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + [[package]] name = "sha1" version = "0.10.5" @@ -4995,6 +5003,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "topological-sort" version = "0.2.2" @@ -5358,7 +5400,6 @@ dependencies = [ "idna", "matches", "percent-encoding", - "serde", ] [[package]] @@ -5685,6 +5726,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + [[package]] name = "writeable" version = "0.5.1" From f9e3b180b7cbee429c465408bbe0e8dbfc104cd7 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 2 Jun 2023 13:47:02 +0330 Subject: [PATCH 618/806] Add enum, reference, array and slice to `render_const_scalar` --- crates/hir-ty/src/display.rs | 253 ++++++++++++++++++------- crates/hir-ty/src/lib.rs | 24 ++- crates/hir-ty/src/mir/eval.rs | 115 ++++++++--- crates/hir-ty/src/mir/lower.rs | 2 +- crates/hir-ty/src/utils.rs | 50 ++++- crates/hir/src/lib.rs | 8 - crates/ide/src/hover/tests.rs | 205 ++++++++++++++++++++ crates/ide/src/inlay_hints/chaining.rs | 12 +- crates/test-utils/src/minicore.rs | 7 + 9 files changed, 561 insertions(+), 115 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 058d5059b1692..a401c49ed21d8 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -16,21 +16,26 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, + EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use intern::{Internable, Interned}; use itertools::Itertools; +use la_arena::ArenaMap; use smallvec::SmallVec; use stdx::never; use crate::{ + consteval::try_const_usize, db::HirDatabase, - from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, + from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, + layout::Layout, + lt_from_placeholder_idx, mapping::from_chalk, mir::pad16, primitive, to_assoc_type_id, - utils::{self, generics, ClosureSubst}, + utils::{self, detect_variant_from_bytes, generics, ClosureSubst}, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, @@ -469,7 +474,7 @@ fn render_const_scalar( // infrastructure and have it here as a field on `f`. let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); match ty.kind(Interner) { - chalk_ir::TyKind::Scalar(s) => match s { + TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), Scalar::Char => { let x = u128::from_le_bytes(pad16(b, false)) as u32; @@ -497,17 +502,54 @@ fn render_const_scalar( } }, }, - chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) { - chalk_ir::TyKind::Str => { + TyKind::Ref(_, _, t) => match t.kind(Interner) { + TyKind::Str => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); - let bytes = memory_map.memory.get(&addr).map(|x| &**x).unwrap_or(&[]); - let s = std::str::from_utf8(bytes).unwrap_or(""); + let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + let s = std::str::from_utf8(&bytes).unwrap_or(""); write!(f, "{s:?}") } - _ => f.write_str(""), + TyKind::Slice(ty) => { + let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); + let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size_one * count) else { + return f.write_str(""); + }; + f.write_str("&[")?; + let mut first = true; + for i in 0..count { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + _ => { + let addr = usize::from_le_bytes(b.try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + f.write_str("&")?; + render_const_scalar(f, bytes, memory_map, t) + } }, - chalk_ir::TyKind::Tuple(_, subst) => { - let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else { + TyKind::Tuple(_, subst) => { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { return f.write_str(""); }; f.write_str("(")?; @@ -529,69 +571,144 @@ fn render_const_scalar( } f.write_str(")") } - chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { - hir_def::AdtId::StructId(s) => { - let data = f.db.struct_data(s); - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { - return f.write_str(""); - }; - match data.variant_data.as_ref() { - VariantData::Record(fields) | VariantData::Tuple(fields) => { - let field_types = f.db.field_types(s.into()); - let krate = adt.0.module(f.db.upcast()).krate(); - let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { - let offset = layout - .fields - .offset(u32::from(id.into_raw()) as usize) - .bytes_usize(); - let ty = field_types[id].clone().substitute(Interner, subst); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { - return f.write_str(""); - }; - let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) - }; - let mut it = fields.iter(); - if matches!(data.variant_data.as_ref(), VariantData::Record(_)) { - write!(f, "{} {{", data.name.display(f.db.upcast()))?; - if let Some((id, data)) = it.next() { - write!(f, " {}: ", data.name.display(f.db.upcast()))?; - render_field(f, id)?; - } - for (id, data) in it { - write!(f, ", {}: ", data.name.display(f.db.upcast()))?; - render_field(f, id)?; - } - write!(f, " }}")?; - } else { - let mut it = it.map(|x| x.0); - write!(f, "{}(", data.name.display(f.db.upcast()))?; - if let Some(id) = it.next() { - render_field(f, id)?; - } - for id in it { - write!(f, ", ")?; - render_field(f, id)?; - } - write!(f, ")")?; - } - return Ok(()); - } - VariantData::Unit => write!(f, "{}", data.name.display(f.db.upcast())), + TyKind::Adt(adt, subst) => { + let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { + return f.write_str(""); + }; + match adt.0 { + hir_def::AdtId::StructId(s) => { + let data = f.db.struct_data(s); + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = f.db.field_types(s.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &layout, + subst, + b, + memory_map, + ) + } + hir_def::AdtId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) + } + hir_def::AdtId::EnumId(e) => { + let Some((var_id, var_layout)) = + detect_variant_from_bytes(&layout, f.db, krate, b, e) else { + return f.write_str(""); + }; + let data = &f.db.enum_data(e).variants[var_id]; + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = + f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &var_layout, + subst, + b, + memory_map, + ) } } - hir_def::AdtId::UnionId(u) => { - write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) - } - hir_def::AdtId::EnumId(_) => f.write_str(""), - }, - chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f), - chalk_ir::TyKind::Raw(_, _) => { + } + TyKind::FnDef(..) => ty.hir_fmt(f), + TyKind::Function(_) | TyKind::Raw(_, _) => { let x = u128::from_le_bytes(pad16(b, false)); write!(f, "{:#X} as ", x)?; ty.hir_fmt(f) } - _ => f.write_str(""), + TyKind::Array(ty, len) => { + let Some(len) = try_const_usize(f.db, len) else { + return f.write_str(""); + }; + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + f.write_str("[")?; + let mut first = true; + for i in 0..len as usize { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + TyKind::Never => f.write_str("!"), + TyKind::Closure(_, _) => f.write_str(""), + TyKind::Generator(_, _) => f.write_str(""), + TyKind::GeneratorWitness(_, _) => f.write_str(""), + // The below arms are unreachable, since const eval will bail out before here. + TyKind::Foreign(_) => f.write_str(""), + TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Alias(_) + | TyKind::AssociatedType(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => f.write_str(""), + // The below arms are unreachable, since we handled them in ref case. + TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str(""), + } +} + +fn render_variant_after_name( + data: &VariantData, + f: &mut HirFormatter<'_>, + field_types: &ArenaMap>, + krate: CrateId, + layout: &Layout, + subst: &Substitution, + b: &[u8], + memory_map: &MemoryMap, +) -> Result<(), HirDisplayError> { + match data { + VariantData::Record(fields) | VariantData::Tuple(fields) => { + let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { + let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); + let ty = field_types[id].clone().substitute(Interner, subst); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) + }; + let mut it = fields.iter(); + if matches!(data, VariantData::Record(_)) { + write!(f, " {{")?; + if let Some((id, data)) = it.next() { + write!(f, " {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + for (id, data) in it { + write!(f, ", {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + write!(f, " }}")?; + } else { + let mut it = it.map(|x| x.0); + write!(f, "(")?; + if let Some(id) = it.next() { + render_field(f, id)?; + } + for id in it { + write!(f, ", ")?; + render_field(f, id)?; + } + write!(f, ")")?; + } + return Ok(()); + } + VariantData::Unit => Ok(()), } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 55803960e1a09..1a4d003bf5e99 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -35,7 +35,10 @@ mod tests; #[cfg(test)] mod test_db; -use std::{collections::HashMap, hash::Hash}; +use std::{ + collections::{hash_map::Entry, HashMap}, + hash::Hash, +}; use chalk_ir::{ fold::{Shift, TypeFoldable}, @@ -160,7 +163,16 @@ pub struct MemoryMap { impl MemoryMap { fn insert(&mut self, addr: usize, x: Vec) { - self.memory.insert(addr, x); + match self.memory.entry(addr) { + Entry::Occupied(mut e) => { + if e.get().len() < x.len() { + e.insert(x); + } + } + Entry::Vacant(e) => { + e.insert(x); + } + } } /// This functions convert each address by a function `f` which gets the byte intervals and assign an address @@ -172,6 +184,14 @@ impl MemoryMap { ) -> Result, MirEvalError> { self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() } + + fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> { + if size == 0 { + Some(&[]) + } else { + self.memory.get(&addr)?.get(0..size) + } + } } /// A concrete constant value diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 28e7759db39a1..6e26d1f22aadd 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -30,7 +30,7 @@ use crate::{ method_resolution::{is_dyn_method, lookup_impl_method}, name, static_lifetime, traits::FnTrait, - utils::ClosureSubst, + utils::{detect_variant_from_bytes, ClosureSubst}, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; @@ -1536,36 +1536,99 @@ impl Evaluator<'_> { } 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_align_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")? + fn rec( + this: &Evaluator<'_>, + bytes: &[u8], + ty: &Ty, + locals: &Locals<'_>, + mm: &mut MemoryMap, + ) -> Result<()> { + match ty.kind(Interner) { + TyKind::Ref(_, _, t) => { + let size = this.size_align_of(t, locals)?; + match size { + Some((size, _)) => { + let addr_usize = from_bytes!(usize, bytes); + mm.insert( + addr_usize, + this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), + ) + } + None => { + let mut check_inner = None; + let element_size = match t.kind(Interner) { + TyKind::Str => 1, + TyKind::Slice(t) => { + check_inner = Some(t); + this.size_of_sized(t, locals, "slice inner type")? + } + _ => return Ok(()), // FIXME: support other kind of unsized types + }; + let (addr, meta) = bytes.split_at(bytes.len() / 2); + let count = from_bytes!(usize, meta); + let size = element_size * count; + let addr = Address::from_bytes(addr)?; + let b = this.read_memory(addr, size)?; + mm.insert(addr.to_usize(), b.to_vec()); + if let Some(ty) = check_inner { + for i in 0..count { + let offset = element_size * i; + rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + } } - _ => 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()); + } + } + } + chalk_ir::TyKind::Tuple(_, subst) => { + let layout = this.layout(ty)?; + for (id, ty) in subst.iter(Interner).enumerate() { + let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + let offset = layout.fields.offset(id).bytes_usize(); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; } } + chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { + AdtId::StructId(s) => { + let data = this.db.struct_data(s); + let layout = this.layout(ty)?; + let field_types = this.db.field_types(s.into()); + for (f, _) in data.variant_data.fields().iter() { + let offset = layout + .fields + .offset(u32::from(f.into_raw()) as usize) + .bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + AdtId::EnumId(e) => { + let layout = this.layout(ty)?; + if let Some((v, l)) = + detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e) + { + let data = &this.db.enum_data(e).variants[v].variant_data; + let field_types = this + .db + .field_types(EnumVariantId { parent: e, local_id: v }.into()); + for (f, _) in data.fields().iter() { + let offset = + l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + } + AdtId::UnionId(_) => (), + }, + _ => (), } - _ => (), + Ok(()) } + let mut mm = MemoryMap::default(); + rec(self, bytes, ty, locals, &mut mm)?; Ok(mm) } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 6fe157f45cfe6..5ed95133356cd 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1855,7 +1855,7 @@ pub fn lower_to_mir( } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local - ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() }); + ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) }); let binding_picker = |b: BindingId| { if root_expr == body.body_expr { body[b].owner.is_none() diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index f60b4607f224c..8f36188b78a3d 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -20,8 +20,8 @@ use hir_def::{ lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, - TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, + LocalEnumVariantId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -30,8 +30,12 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use crate::{ - consteval::unknown_const, db::HirDatabase, ChalkTraitId, Const, ConstScalar, GenericArg, - Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, + consteval::unknown_const, + db::HirDatabase, + layout::{Layout, TagEncoding}, + mir::pad16, + ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, + Ty, TyExt, WhereClause, }; pub(crate) fn fn_traits( @@ -440,3 +444,41 @@ impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { Ok(constant) } } + +pub(crate) fn detect_variant_from_bytes<'a>( + layout: &'a Layout, + db: &dyn HirDatabase, + krate: CrateId, + b: &[u8], + e: EnumId, +) -> Option<(LocalEnumVariantId, &'a Layout)> { + let (var_id, var_layout) = match &layout.variants { + hir_def::layout::Variants::Single { index } => (index.0, &*layout), + hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => { + let target_data_layout = db.target_data_layout(krate)?; + 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 + let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false)); + match tag_encoding { + TagEncoding::Direct => { + let x = variants.iter_enumerated().find(|x| { + db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 }) + == Ok(tag) + })?; + (x.0 .0, x.1) + } + TagEncoding::Niche { untagged_variant, niche_start, .. } => { + let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize; + let variant = variants + .iter_enumerated() + .map(|(x, _)| x) + .filter(|x| x != untagged_variant) + .nth(candidate_tag) + .unwrap_or(*untagged_variant); + (variant.0, &variants[variant]) + } + } + } + }; + Some((var_id, var_layout)) +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7c432197a60a7..9e85690ec5693 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2107,14 +2107,6 @@ impl Const { pub fn render_eval(self, db: &dyn HirDatabase) -> Result { let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; 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); } } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f2ee79a23e6f3..d2c035c471e43 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -4337,6 +4337,211 @@ const FOO$0: f64 = 1.0f64; ); } +#[test] +fn hover_const_eval_enum() { + check( + r#" +enum Enum { + V1, + V2, +} + +const VX: Enum = Enum::V1; + +const FOO$0: Enum = VX; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Enum = V1 + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option = Some(2); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option = Some(2) + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option<&i32> = Some(2).as_ref(); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option<&i32> = Some(&2) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_slice() { + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32] = &[1, 2, 3 + 4]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32] = &[1, 2, 7] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32; 5] = &[12; 5]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32; 5] = &[12, 12, 12, 12, 12] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +const FOO$0: (&i32, &[i32], &i32) = { + let a: &[i32] = &[1, 2, 3]; + (&a[0], a, &a[0]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1) + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +struct Tree(&[Tree]); + +const FOO$0: Tree = { + let x = &[Tree(&[]), Tree(&[Tree(&[])])]; + Tree(&[Tree(x), Tree(x)]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])]) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_str() { + check( + r#" +const FOO$0: &str = "foo"; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &str = "foo" + ``` + "#]], + ); + check( + r#" +struct X { + a: &'static str, + b: &'static str, +} +const FOO$0: X = X { + a: "axiom", + b: "buy N large", +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: X = X { a: "axiom", b: "buy N large" } + ``` + "#]], + ); + check( + r#" +const FOO$0: (&str, &str) = { + let x = "foo"; + (x, x) +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&str, &str) = ("foo", "foo") + ``` + "#]], + ); +} + #[test] fn hover_const_eval_in_generic_trait() { // Doesn't compile, but we shouldn't crash. diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 9f49344924958..ce1e03a069b24 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 7f4838888bd79..c9e85e36870f9 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -941,6 +941,13 @@ pub mod option { } } + pub const fn as_ref(&self) -> Option<&T> { + match self { + Some(x) => Some(x), + None => None, + } + } + pub fn and(self, optb: Option) -> Option { loop {} } From c0e9b5737153a15f15d400a26114ccb067a51e9d Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Fri, 26 May 2023 11:59:44 +0200 Subject: [PATCH 619/806] Improve assist to filter invalid params The change updates the logic to determine if a function parameter is valid for replacing the type param with the trait implementation. First all usages are determined, to check if they are used outside the function parameter list. If an outside reference is found, e.g. in body, return type or where clause, the assist is skipped. All remaining usages only appear in the function param list. For each usage the param type is checked to see if it's valid. **Please note** the logic currently follows a heuristic and may not cover all existing parameter declarations. * determine valid usage references by checking ancestors (on AST level) * split test into separate ones --- .../replace_named_generic_with_impl.rs | 238 ++++++++++++++---- 1 file changed, 188 insertions(+), 50 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index f32ceb42654bb..e7b62d49bb814 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -2,13 +2,17 @@ use hir::Semantics; use ide_db::{ base_db::{FileId, FileRange}, defs::Definition, - search::SearchScope, + search::{SearchScope, UsageSearchResult}, RootDatabase, }; use syntax::{ - ast::{self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds}, - ted, AstNode, + ast::{ + self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, + PathType, + }, + match_ast, ted, AstNode, }; +use text_edit::TextRange; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -36,55 +40,46 @@ pub(crate) fn replace_named_generic_with_impl( let type_bound_list = type_param.type_bound_list()?; let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; - let params = fn_ - .param_list()? - .params() - .filter_map(|param| { - // function parameter type needs to match generic type name - if let ast::Type::PathType(path_type) = param.ty()? { - let left = path_type.path()?.segment()?.name_ref()?.ident_token()?.to_string(); - let right = type_param_name.to_string(); - if left == right { - Some(param) - } else { - None - } - } else { - None - } - }) - .collect::>(); - - if params.is_empty() { - return None; - } + let param_list_text_range = fn_.param_list()?.syntax().text_range(); let type_param_hir_def = ctx.sema.to_def(&type_param)?; let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param_hir_def)); - if is_referenced_outside(&ctx.sema, type_param_def, &fn_, ctx.file_id()) { + // get all usage references for the type param + let usage_refs = find_usages(&ctx.sema, &fn_, type_param_def, ctx.file_id()); + if usage_refs.is_empty() { return None; } + // All usage references need to be valid (inside the function param list) + if !check_valid_usages(&usage_refs, param_list_text_range) { + return None; + } + + let mut path_types_to_replace = Vec::new(); + for (_a, refs) in usage_refs.iter() { + for usage_ref in refs { + let param_node = find_path_type(&ctx.sema, &type_param_name, &usage_ref.name)?; + path_types_to_replace.push(param_node); + } + } + let target = type_param.syntax().text_range(); acc.add( AssistId("replace_named_generic_with_impl", AssistKind::RefactorRewrite), - "Replace named generic with impl", + "Replace named generic with impl trait", target, |edit| { let type_param = edit.make_mut(type_param); let fn_ = edit.make_mut(fn_); - // get all params - let param_types = params - .iter() - .filter_map(|param| match param.ty() { - Some(ast::Type::PathType(param_type)) => Some(edit.make_mut(param_type)), - _ => None, - }) + let path_types_to_replace = path_types_to_replace + .into_iter() + .map(|param| edit.make_mut(param)) .collect::>(); + // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); if generic_params.generic_params().count() == 0 { @@ -92,31 +87,84 @@ pub(crate) fn replace_named_generic_with_impl( } } - // get type bounds in signature type: `P` -> `impl AsRef` let new_bounds = impl_trait_type(type_bound_list); - for param_type in param_types.iter().rev() { - ted::replace(param_type.syntax(), new_bounds.clone_for_update().syntax()); + for path_type in path_types_to_replace.iter().rev() { + ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); } }, ) } -fn is_referenced_outside( +fn find_path_type( + sema: &Semantics<'_, RootDatabase>, + type_param_name: &Name, + param: &NameLike, +) -> Option { + let path_type = + sema.ancestors_with_macros(param.syntax().clone()).find_map(ast::PathType::cast)?; + + // Ignore any path types that look like `P::Assoc` + if path_type.path()?.as_single_name_ref()?.text() != type_param_name.text() { + return None; + } + + let ancestors = sema.ancestors_with_macros(path_type.syntax().clone()); + + let mut in_generic_arg_list = false; + let mut is_associated_type = false; + + // walking the ancestors checks them in a heuristic way until the `Fn` node is reached. + for ancestor in ancestors { + match_ast! { + match ancestor { + ast::PathSegment(ps) => { + match ps.kind()? { + ast::PathSegmentKind::Name(_name_ref) => (), + ast::PathSegmentKind::Type { .. } => return None, + _ => return None, + } + }, + ast::GenericArgList(_) => { + in_generic_arg_list = true; + }, + ast::AssocTypeArg(_) => { + is_associated_type = true; + }, + ast::ImplTraitType(_) => { + if in_generic_arg_list && !is_associated_type { + return None; + } + }, + ast::DynTraitType(_) => { + if !is_associated_type { + return None; + } + }, + ast::Fn(_) => return Some(path_type), + _ => (), + } + } + } + + None +} + +/// Returns all usage references for the given type parameter definition. +fn find_usages( sema: &Semantics<'_, RootDatabase>, - type_param_def: Definition, fn_: &ast::Fn, + type_param_def: Definition, file_id: FileId, -) -> bool { - // limit search scope to function body & return type - let search_ranges = vec![ - fn_.body().map(|body| body.syntax().text_range()), - fn_.ret_type().map(|ret_type| ret_type.syntax().text_range()), - ]; - - search_ranges.into_iter().flatten().any(|search_range| { - let file_range = FileRange { file_id, range: search_range }; - !type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all().is_empty() - }) +) -> UsageSearchResult { + let file_range = FileRange { file_id, range: fn_.syntax().text_range() }; + type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all() +} + +fn check_valid_usages(usages: &UsageSearchResult, param_list_range: TextRange) -> bool { + usages + .iter() + .flat_map(|(_, usage_refs)| usage_refs) + .all(|usage_ref| param_list_range.contains_range(usage_ref.range)) } #[cfg(test)] @@ -152,6 +200,96 @@ mod tests { ); } + #[test] + fn replace_generic_trait_applies_to_generic_arguments_in_params() { + check_assist( + replace_named_generic_with_impl, + r#" + fn foo( + _: P, + _: Option

, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + ) {} + "#, + r#" + fn foo( + _: impl Trait, + _: Option, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + ) {} + "#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_one_param_type_is_invalid() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#" + fn foo( + _: i32, + _: Option

, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + _:

::Assoc, + ) {} + "#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_referenced_in_where_clause() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo() where I: FromRef

{}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_with_type_alias() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(p:

::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_as_argument_in_outer_trait_alias() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: <() as OtherTrait

>::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_with_inner_associated_type() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: P::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_passed_into_outer_impl_trait() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: impl OtherTrait

) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_in_passed_function_parameter() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: &dyn Fn(P)) {}"#, + ); + } + #[test] fn replace_generic_with_multiple_generic_params() { check_assist( From 653f9c7f28d8b5669e1f64f9210db92335151e78 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 2 Jun 2023 13:51:01 +0200 Subject: [PATCH 620/806] Add rustdoc test for double-hyphen to dash doc comment conversion --- tests/rustdoc/double-hyphen-to-dash.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/rustdoc/double-hyphen-to-dash.rs diff --git a/tests/rustdoc/double-hyphen-to-dash.rs b/tests/rustdoc/double-hyphen-to-dash.rs new file mode 100644 index 0000000000000..66905f90cfc93 --- /dev/null +++ b/tests/rustdoc/double-hyphen-to-dash.rs @@ -0,0 +1,9 @@ +// This test ensures that `--` (double-hyphen) is correctly converted into `–` (dash). + +#![crate_name = "foo"] + +// @has 'foo/index.html' '//*[@class="desc docblock-short"]' '–' +// @has 'foo/struct.Bar.html' '//*[@class="docblock"]' '–' + +/// -- +pub struct Bar; From 9df45722775774e2d3c30638cf1491e418ae4157 Mon Sep 17 00:00:00 2001 From: Kourosh Date: Fri, 2 Jun 2023 15:30:40 +0330 Subject: [PATCH 621/806] Fix typo in `std::cell` module docs --- library/core/src/cell.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 744767aae44ca..c1cc892eb8607 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -59,7 +59,7 @@ //! [`borrow`](`RefCell::borrow`), and a mutable borrow (`&mut T`) can be obtained with //! [`borrow_mut`](`RefCell::borrow_mut`). When these functions are called, they first verify that //! Rust's borrow rules will be satisfied: any number of immutable borrows are allowed or a -//! single immutable borrow is allowed, but never both. If a borrow is attempted that would violate +//! single mutable borrow is allowed, but never both. If a borrow is attempted that would violate //! these rules, the thread will panic. //! //! The corresponding [`Sync`] version of `RefCell` is [`RwLock`]. From a6a27a7ff84cd23bbb9ad65ae2c06c2715247ca2 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 2 Jun 2023 17:29:17 +0330 Subject: [PATCH 622/806] Support floating point intrinsics in const eval --- .../hir-ty/src/consteval/tests/intrinsics.rs | 34 ++ crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir-ty/src/mir/eval/shim.rs | 341 ++++++++++++------ crates/ide/src/hover/tests.rs | 24 ++ 4 files changed, 294 insertions(+), 107 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 1feb9a4441c03..e05d824dbacfc 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -174,6 +174,40 @@ fn likely() { ); } +#[test] +fn floating_point() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn sqrtf32(x: f32) -> f32; + pub fn powf32(a: f32, x: f32) -> f32; + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; + } + + const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4); + "#, + i128::from_le_bytes(pad16( + &f32::to_le_bytes(1.2f32.sqrt() + 3.4f32.powf(5.6) + (-7.8f32).mul_add(1.3, 2.4)), + true, + )), + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn powif64(a: f64, x: i32) -> f64; + pub fn sinf64(x: f64) -> f64; + pub fn minnumf64(x: f64, y: f64) -> f64; + } + + const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3); + "#, + i128::from_le_bytes(pad16( + &f64::to_le_bytes(1.2f64.powi(5) + 3.4f64.sin() + (-7.8f64).min(1.3)), + true, + )), + ); +} + #[test] fn atomic() { check_number( diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 6e26d1f22aadd..6d7701c9e8d4b 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -870,7 +870,7 @@ impl Evaluator<'_> { Owned(c.to_le_bytes().into()) } chalk_ir::FloatTy::F64 => { - let c = -from_bytes!(f32, c); + let c = -from_bytes!(f64, c); Owned(c.to_le_bytes().into()) } } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index e05004eeb6a86..77ee7b6b6e2a8 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -316,119 +316,145 @@ impl Evaluator<'_> { fn exec_intrinsic( &mut self, - as_str: &str, + name: &str, args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, locals: &Locals<'_>, span: MirSpan, ) -> Result<()> { - // We are a single threaded runtime with no UB checking and no optimization, so - // we can implement these as normal functions. - if let Some(name) = as_str.strip_prefix("atomic_") { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided")); - }; - let Some(arg0) = args.get(0) else { - return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided")); - }; - let arg0_addr = Address::from_bytes(arg0.get(self)?)?; - let arg0_interval = Interval::new( - arg0_addr, - self.size_of_sized(ty, locals, "atomic intrinsic type arg")?, - ); - if name.starts_with("load_") { - return destination.write_from_interval(self, arg0_interval); - } - let Some(arg1) = args.get(1) else { - return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided")); + if let Some(name) = name.strip_prefix("atomic_") { + return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span); + } + if let Some(name) = name.strip_suffix("f64") { + let result = match name { + "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" + | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64")); + }; + let arg = from_bytes!(f64, arg.get(self)?); + match name { + "sqrt" => arg.sqrt(), + "sin" => arg.sin(), + "cos" => arg.cos(), + "exp" => arg.exp(), + "exp2" => arg.exp2(), + "log" => arg.ln(), + "log10" => arg.log10(), + "log2" => arg.log2(), + "fabs" => arg.abs(), + "floor" => arg.floor(), + "ceil" => arg.ceil(), + "trunc" => arg.trunc(), + // FIXME: these rounds should be different, but only `.round()` is stable now. + "rint" => arg.round(), + "nearbyint" => arg.round(), + "round" => arg.round(), + "roundeven" => arg.round(), + _ => unreachable!(), + } + } + "pow" | "minnum" | "maxnum" | "copysign" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(f64, arg2.get(self)?); + match name { + "pow" => arg1.powf(arg2), + "minnum" => arg1.min(arg2), + "maxnum" => arg1.max(arg2), + "copysign" => arg1.copysign(arg2), + _ => unreachable!(), + } + } + "powi" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(i32, arg2.get(self)?); + arg1.powi(arg2) + } + "fma" => { + let [arg1, arg2, arg3] = args else { + return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(f64, arg2.get(self)?); + let arg3 = from_bytes!(f64, arg3.get(self)?); + arg1.mul_add(arg2, arg3) + } + _ => not_supported!("unknown f64 intrinsic {name}"), }; - if name.starts_with("store_") { - return arg0_interval.write_from_interval(self, arg1.interval); - } - if name.starts_with("xchg_") { - destination.write_from_interval(self, arg0_interval)?; - return arg0_interval.write_from_interval(self, arg1.interval); - } - if name.starts_with("xadd_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = lhs.wrapping_add(rhs); - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - if name.starts_with("xsub_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = lhs.wrapping_sub(rhs); - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - if name.starts_with("and_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = lhs & rhs; - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - if name.starts_with("or_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = lhs | rhs; - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - if name.starts_with("xor_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = lhs ^ rhs; - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - if name.starts_with("nand_") { - destination.write_from_interval(self, arg0_interval)?; - let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); - let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); - let ans = !(lhs & rhs); - return arg0_interval - .write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); - } - let Some(arg2) = args.get(2) else { - return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided")); + return destination.write_from_bytes(self, &result.to_le_bytes()); + } + if let Some(name) = name.strip_suffix("f32") { + let result = match name { + "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" + | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32")); + }; + let arg = from_bytes!(f32, arg.get(self)?); + match name { + "sqrt" => arg.sqrt(), + "sin" => arg.sin(), + "cos" => arg.cos(), + "exp" => arg.exp(), + "exp2" => arg.exp2(), + "log" => arg.ln(), + "log10" => arg.log10(), + "log2" => arg.log2(), + "fabs" => arg.abs(), + "floor" => arg.floor(), + "ceil" => arg.ceil(), + "trunc" => arg.trunc(), + // FIXME: these rounds should be different, but only `.round()` is stable now. + "rint" => arg.round(), + "nearbyint" => arg.round(), + "round" => arg.round(), + "roundeven" => arg.round(), + _ => unreachable!(), + } + } + "pow" | "minnum" | "maxnum" | "copysign" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(f32, arg2.get(self)?); + match name { + "pow" => arg1.powf(arg2), + "minnum" => arg1.min(arg2), + "maxnum" => arg1.max(arg2), + "copysign" => arg1.copysign(arg2), + _ => unreachable!(), + } + } + "powi" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(i32, arg2.get(self)?); + arg1.powi(arg2) + } + "fma" => { + let [arg1, arg2, arg3] = args else { + return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(f32, arg2.get(self)?); + let arg3 = from_bytes!(f32, arg3.get(self)?); + arg1.mul_add(arg2, arg3) + } + _ => not_supported!("unknown f32 intrinsic {name}"), }; - if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") { - let dest = if arg1.get(self)? == arg0_interval.get(self)? { - arg0_interval.write_from_interval(self, arg2.interval)?; - (arg1.interval, true) - } else { - (arg0_interval, false) - }; - let result_ty = TyKind::Tuple( - 2, - Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), - ) - .intern(Interner); - let layout = self.layout(&result_ty)?; - let result = self.make_by_layout( - layout.size.bytes_usize(), - &layout, - None, - [ - IntervalOrOwned::Borrowed(dest.0), - IntervalOrOwned::Owned(vec![u8::from(dest.1)]), - ] - .into_iter(), - )?; - return destination.write_from_bytes(self, &result); - } - not_supported!("unknown atomic intrinsic {name}"); + return destination.write_from_bytes(self, &result.to_le_bytes()); } - match as_str { + match name { "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")); @@ -539,7 +565,7 @@ impl Evaluator<'_> { self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?; let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); - let (ans, u128overflow) = match as_str { + let (ans, u128overflow) = match name { "add_with_overflow" => lhs.overflowing_add(rhs), "sub_with_overflow" => lhs.overflowing_sub(rhs), "mul_with_overflow" => lhs.overflowing_mul(rhs), @@ -641,7 +667,110 @@ impl Evaluator<'_> { } self.exec_fn_trait(&args, destination, locals, span) } - _ => not_supported!("unknown intrinsic {as_str}"), + _ => not_supported!("unknown intrinsic {name}"), + } + } + + fn exec_atomic_intrinsic( + &mut self, + name: &str, + args: &[IntervalAndTy], + generic_args: &Substitution, + destination: Interval, + locals: &Locals<'_>, + _span: MirSpan, + ) -> Result<()> { + // We are a single threaded runtime with no UB checking and no optimization, so + // we can implement these as normal functions. + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided")); + }; + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided")); + }; + let arg0_addr = Address::from_bytes(arg0.get(self)?)?; + let arg0_interval = + Interval::new(arg0_addr, self.size_of_sized(ty, locals, "atomic intrinsic type arg")?); + if name.starts_with("load_") { + return destination.write_from_interval(self, arg0_interval); + } + let Some(arg1) = args.get(1) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided")); + }; + if name.starts_with("store_") { + return arg0_interval.write_from_interval(self, arg1.interval); + } + if name.starts_with("xchg_") { + destination.write_from_interval(self, arg0_interval)?; + return arg0_interval.write_from_interval(self, arg1.interval); + } + if name.starts_with("xadd_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs.wrapping_add(rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("xsub_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs.wrapping_sub(rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("and_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs & rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("or_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs | rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("xor_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs ^ rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("nand_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = !(lhs & rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + let Some(arg2) = args.get(2) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided")); + }; + if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") { + let dest = if arg1.get(self)? == arg0_interval.get(self)? { + arg0_interval.write_from_interval(self, arg2.interval)?; + (arg1.interval, true) + } else { + (arg0_interval, false) + }; + let result_ty = TyKind::Tuple( + 2, + Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), + ) + .intern(Interner); + let layout = self.layout(&result_ty)?; + let result = self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + [IntervalOrOwned::Borrowed(dest.0), IntervalOrOwned::Owned(vec![u8::from(dest.1)])] + .into_iter(), + )?; + return destination.write_from_bytes(self, &result); } + not_supported!("unknown atomic intrinsic {name}"); } } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index d2c035c471e43..a2f96977581c2 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -4337,6 +4337,30 @@ const FOO$0: f64 = 1.0f64; ); } +#[test] +fn hover_const_eval_floating_point() { + check( + r#" +extern "rust-intrinsic" { + pub fn expf64(x: f64) -> f64; +} + +const FOO$0: f64 = expf64(1.2); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: f64 = 3.3201169227365472 + ``` + "#]], + ); +} + #[test] fn hover_const_eval_enum() { check( From bdb475cf6c5712e706146635c4dd8bf6b2e506ff Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Sat, 18 Mar 2023 18:45:51 +0100 Subject: [PATCH 623/806] Retry to fork/spawn with exponential backoff --- .../std/src/sys/unix/process/process_unix.rs | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 612d43fe20414..f68f254190254 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -35,8 +35,20 @@ cfg_if::cfg_if! { if #[cfg(all(target_os = "nto", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - // arbitrary number of tries: - const MAX_FORKSPAWN_TRIES: u32 = 4; + use crate::time::Duration; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); } } @@ -163,12 +175,24 @@ impl Command { unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { use crate::sys::os::errno; - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut minimum_delay = None; + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { let r = libc::fork(); - if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { - thread::yield_now(); - tries_left -= 1; + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if minimum_delay.is_none() { + minimum_delay = Some(get_clock_resolution()); + } + if delay < minimum_delay.unwrap() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else { + thread::sleep(delay); + } + delay *= 2; + continue; } else { return cvt(r).map(|res| (res, -1)); } @@ -481,12 +505,22 @@ impl Command { argv: *const *mut c_char, envp: *const *mut c_char, ) -> i32 { - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut minimum_delay = None; + let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF if tries_left > 0 => { - thread::yield_now(); - tries_left -= 1; + libc::EBADF => { + if minimum_delay.is_none() { + minimum_delay = Some(get_clock_resolution()); + } + if delay < minimum_delay.unwrap() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else { + thread::sleep(delay); + } + delay *= 2; continue; } r => { From d8f21101ec383d33c68f1867faf2ba74b3dca1a8 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Sun, 19 Mar 2023 11:00:18 +0100 Subject: [PATCH 624/806] Remove "one thread in tests" limitation in nto-qnx.md --- src/doc/rustc/src/platform-support/nto-qnx.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 0d815c9b59807..b376c4a84ac9e 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -164,18 +164,12 @@ export exclude_tests=' --exclude tests/run-make-fulldeps' env $build_env \ - ./x.py test -j 1 \ + ./x.py test \ $exclude_tests \ --stage 1 \ --target x86_64-pc-nto-qnx710 ``` -Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`. -See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html) -(error section) for more information. -This can be achieved by using the `-j 1` parameter in the `x.py` call. -This issue is being researched and we will try to allow parallelism in the future. - ## Building Rust programs Rust does not yet ship pre-compiled artifacts for this target. From 716cc5ac938ad0a5302fe924a7142c0cc1d7d515 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Fri, 2 Jun 2023 17:52:14 +0200 Subject: [PATCH 625/806] Only determine clock res once; give up before sleeping more than 1 second --- .../std/src/sys/unix/process/process_unix.rs | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index f68f254190254..3721988b405b4 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -36,19 +36,25 @@ cfg_if::cfg_if! { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; + use crate::sync::LazyLock; // Get smallest amount of time we can sleep. // Return a common value if it cannot be determined. fn get_clock_resolution() -> Duration { - let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 - { - Duration::from_nanos(mindelay.tv_nsec as u64) - } else { - Duration::from_millis(1) - } + static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY } // Arbitrary minimum sleep duration for retrying fork/spawn const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); } } @@ -175,21 +181,22 @@ impl Command { unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { use crate::sys::os::errno; - let mut minimum_delay = None; let mut delay = MIN_FORKSPAWN_SLEEP; loop { let r = libc::fork(); if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { - if minimum_delay.is_none() { - minimum_delay = Some(get_clock_resolution()); - } - if delay < minimum_delay.unwrap() { + if delay < get_clock_resolution() { // We cannot sleep this short (it would be longer). // Yield instead. thread::yield_now(); - } else { + } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); } delay *= 2; continue; @@ -504,27 +511,28 @@ impl Command { attrp: *const posix_spawnattr_t, argv: *const *mut c_char, envp: *const *mut c_char, - ) -> i32 { - let mut minimum_delay = None; + ) -> io::Result { let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { libc::EBADF => { - if minimum_delay.is_none() { - minimum_delay = Some(get_clock_resolution()); - } - if delay < minimum_delay.unwrap() { + if delay < get_clock_resolution() { // We cannot sleep this short (it would be longer). // Yield instead. thread::yield_now(); - } else { + } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); } delay *= 2; continue; } r => { - return r; + return Ok(r); } } } @@ -654,14 +662,20 @@ impl Command { let spawn_fn = libc::posix_spawnp; #[cfg(target_os = "nto")] let spawn_fn = retrying_libc_posix_spawnp; - cvt_nz(spawn_fn( + + let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, - ))?; + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; Ok(Some(p)) } } From 9f70efb31aa9345e07b547b3d4f63a1e08b53c49 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 2 Jun 2023 16:37:51 +0000 Subject: [PATCH 626/806] only suppress coercion error if type is definitely unsized --- compiler/rustc_hir_typeck/src/coercion.rs | 30 ++++++++++++------- tests/ui/typeck/return-dyn-type-mismatch-2.rs | 11 +++++++ .../typeck/return-dyn-type-mismatch-2.stderr | 15 ++++++++++ tests/ui/typeck/return-dyn-type-mismatch.rs | 21 +++++++++++++ .../ui/typeck/return-dyn-type-mismatch.stderr | 15 ++++++++++ 5 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 tests/ui/typeck/return-dyn-type-mismatch-2.rs create mode 100644 tests/ui/typeck/return-dyn-type-mismatch-2.stderr create mode 100644 tests/ui/typeck/return-dyn-type-mismatch.rs create mode 100644 tests/ui/typeck/return-dyn-type-mismatch.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 905781ec8f594..ba49e0c41618a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1595,7 +1595,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Some(blk_id), ); if !fcx.tcx.features().unsized_locals { - unsized_return = self.is_return_ty_unsized(fcx, blk_id); + unsized_return = self.is_return_ty_definitely_unsized(fcx); } if let Some(expression) = expression && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { @@ -1614,8 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { None, ); if !fcx.tcx.features().unsized_locals { - let id = fcx.tcx.hir().parent_id(id); - unsized_return = self.is_return_ty_unsized(fcx, id); + unsized_return = self.is_return_ty_definitely_unsized(fcx); } } _ => { @@ -1896,15 +1895,24 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.help("you could instead create a new `enum` with a variant for each returned type"); } - fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { - if let Some((_, fn_decl, _)) = fcx.get_fn_decl(blk_id) - && let hir::FnRetTy::Return(ty) = fn_decl.output - && let ty = fcx.astconv().ast_ty_to_ty( ty) - && let ty::Dynamic(..) = ty.kind() - { - return true; + /// Checks whether the return type is unsized via an obligation, which makes + /// sure we consider `dyn Trait: Sized` where clauses, which are trivially + /// false but technically valid for typeck. + fn is_return_ty_definitely_unsized(&self, fcx: &FnCtxt<'_, 'tcx>) -> bool { + if let Some(sig) = fcx.body_fn_sig() { + !fcx.predicate_may_hold(&Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + ty::TraitRef::new( + fcx.tcx, + fcx.tcx.require_lang_item(hir::LangItem::Sized, None), + [sig.output()], + ), + )) + } else { + false } - false } pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { diff --git a/tests/ui/typeck/return-dyn-type-mismatch-2.rs b/tests/ui/typeck/return-dyn-type-mismatch-2.rs new file mode 100644 index 0000000000000..328f154dcbc3e --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch-2.rs @@ -0,0 +1,11 @@ +trait Trait {} + +fn foo() -> dyn Trait +where + dyn Trait: Sized, // pesky sized predicate +{ + 42 + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/typeck/return-dyn-type-mismatch-2.stderr b/tests/ui/typeck/return-dyn-type-mismatch-2.stderr new file mode 100644 index 0000000000000..9c368e83834b1 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch-2.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-dyn-type-mismatch-2.rs:7:5 + | +LL | fn foo() -> dyn Trait + | ------------ expected `(dyn Trait + 'static)` because of return type +... +LL | 42 + | ^^ expected `dyn Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/return-dyn-type-mismatch.rs b/tests/ui/typeck/return-dyn-type-mismatch.rs new file mode 100644 index 0000000000000..93718f70f4193 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch.rs @@ -0,0 +1,21 @@ +pub trait TestTrait { + type MyType; + + fn func() -> Option + where + Self: Sized; +} + +impl dyn TestTrait +where + Self: Sized, // pesky sized predicate +{ + fn other_func() -> dyn TestTrait { + match Self::func() { + None => None, + //~^ ERROR mismatched types + } + } +} + +fn main() {} diff --git a/tests/ui/typeck/return-dyn-type-mismatch.stderr b/tests/ui/typeck/return-dyn-type-mismatch.stderr new file mode 100644 index 0000000000000..9d0a609d87f81 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-dyn-type-mismatch.rs:15:21 + | +LL | fn other_func() -> dyn TestTrait { + | ------------------------- expected `(dyn TestTrait + 'static)` because of return type +LL | match Self::func() { +LL | None => None, + | ^^^^ expected `dyn TestTrait`, found `Option<_>` + | + = note: expected trait object `(dyn TestTrait + 'static)` + found enum `Option<_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 4d9b476bb1f63fa25ca9bb7f85b536352c744570 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Fri, 2 Jun 2023 11:40:36 -0500 Subject: [PATCH 627/806] Update dependencies with reported vulnerabilities bumpalo 3.12.1 (yanked) * updated to 3.13.0 tokio 1.8.4 - https://rustsec.org/advisories/RUSTSEC-2023-0001 * updated to 1.28.2 remove_dir_all 0.5.3 - https://rustsec.org/advisories/RUSTSEC-2023-0018 * removed by using the standard library function in `rust-installer` instead and updating to `tempfile@3.5.0` (which also removes the dependency). --- Cargo.lock | 48 +++++++++---------- .../rustc_data_structures/src/temp_dir.rs | 2 +- src/tools/rust-installer/Cargo.toml | 1 - src/tools/rust-installer/src/util.rs | 2 +- src/tools/tidy/src/deps.rs | 5 +- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00bfbadece33c..be622bbb935d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytecount" @@ -1219,7 +1219,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "windows-sys 0.45.0", ] @@ -1761,7 +1761,6 @@ dependencies = [ "flate2", "num_cpus", "rayon", - "remove_dir_all", "tar", "walkdir", "xz2", @@ -2445,7 +2444,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "winapi", ] @@ -2458,7 +2457,7 @@ checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "windows-sys 0.42.0", ] @@ -2801,6 +2800,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.0" @@ -2808,7 +2816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.10", ] [[package]] @@ -2860,15 +2868,6 @@ version = "0.1.0" name = "remote-test-server" version = "0.1.0" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "replace-version-placeholder" version = "0.1.0" @@ -4789,16 +4788,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -4984,14 +4982,14 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.8.4" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dae83881bc9b0403dd5b44ea9deed3e939856cc8722d5be37f0d6e5c6d53dd" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", - "memchr", "pin-project-lite", + "windows-sys 0.48.0", ] [[package]] diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index a780d2386a63c..621d3011a2aa1 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -16,7 +16,7 @@ impl Drop for MaybeTempDir { // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { - dir.into_path(); + let _ = dir.into_path(); } } } diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml index 97734f048ab33..85e979f07bff9 100644 --- a/src/tools/rust-installer/Cargo.toml +++ b/src/tools/rust-installer/Cargo.toml @@ -17,7 +17,6 @@ tar = "0.4.38" walkdir = "2" xz2 = "0.1.4" num_cpus = "1" -remove_dir_all = "0.5" [dependencies.clap] features = ["derive"] diff --git a/src/tools/rust-installer/src/util.rs b/src/tools/rust-installer/src/util.rs index 674617c657c59..6cac314b68d06 100644 --- a/src/tools/rust-installer/src/util.rs +++ b/src/tools/rust-installer/src/util.rs @@ -82,7 +82,7 @@ pub fn open_file>(path: P) -> Result { /// Wraps `remove_dir_all` with a nicer error message. pub fn remove_dir_all>(path: P) -> Result<()> { - remove_dir_all::remove_dir_all(path.as_ref()) + fs::remove_dir_all(path.as_ref()) .with_context(|| format!("failed to remove dir '{}'", path.as_ref().display()))?; Ok(()) } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index db2b7910b711c..3dbf2884ed1f1 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -168,12 +168,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "instant", "intl-memoizer", "intl_pluralrules", + "io-lifetimes", "itertools", "itoa", "jobserver", "lazy_static", "libc", "libloading", + "linux-raw-sys", "litemap", "lock_api", "log", @@ -211,12 +213,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "regex", "regex-automata", "regex-syntax", - "remove_dir_all", "rustc-demangle", "rustc-hash", "rustc-rayon", "rustc-rayon-core", "rustc_version", + "rustix", "ruzstd", // via object in thorin-dwp "ryu", "scoped-tls", @@ -280,6 +282,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc", From 8657a64a68a20cc94f8ff74c8d59bba0827dbdb7 Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Sat, 3 Jun 2023 01:58:48 +0800 Subject: [PATCH 628/806] Add `--lib` to `cargo doc` --- src/bootstrap/doc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 3de85c91516c4..e92a1f89d1645 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -834,6 +834,7 @@ macro_rules! tool_doc { cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. cargo.arg("--no-deps"); + cargo.arg("--lib"); $( cargo.arg("-p").arg($krate); )+ From ecd7809784ddb1e96c9f292347d73d074106e4e9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 2 Jun 2023 19:21:56 +0000 Subject: [PATCH 629/806] Don't ICE in new solver when auto traits have associated types --- .../rustc_trait_selection/src/solve/project_goals.rs | 8 ++++++-- ...ue-23080-2.stderr => issue-23080-2.current.stderr} | 2 +- tests/ui/auto-traits/issue-23080-2.next.stderr | 11 +++++++++++ tests/ui/auto-traits/issue-23080-2.rs | 3 +++ 4 files changed, 21 insertions(+), 3 deletions(-) rename tests/ui/auto-traits/{issue-23080-2.stderr => issue-23080-2.current.stderr} (91%) create mode 100644 tests/ui/auto-traits/issue-23080-2.next.stderr diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 7d7dfa2c83776..becd84e79e12c 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -193,10 +193,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } fn consider_auto_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - bug!("auto traits do not have associated types: {:?}", goal); + ecx.tcx().sess.delay_span_bug( + ecx.tcx().def_span(goal.predicate.def_id()), + "associated types not allowed on auto traits", + ); + Err(NoSolution) } fn consider_trait_alias_candidate( diff --git a/tests/ui/auto-traits/issue-23080-2.stderr b/tests/ui/auto-traits/issue-23080-2.current.stderr similarity index 91% rename from tests/ui/auto-traits/issue-23080-2.stderr rename to tests/ui/auto-traits/issue-23080-2.current.stderr index fed485612daca..a57c6d9b0cbf9 100644 --- a/tests/ui/auto-traits/issue-23080-2.stderr +++ b/tests/ui/auto-traits/issue-23080-2.current.stderr @@ -1,5 +1,5 @@ error[E0380]: auto traits cannot have associated items - --> $DIR/issue-23080-2.rs:5:10 + --> $DIR/issue-23080-2.rs:8:10 | LL | unsafe auto trait Trait { | ----- auto traits cannot have associated items diff --git a/tests/ui/auto-traits/issue-23080-2.next.stderr b/tests/ui/auto-traits/issue-23080-2.next.stderr new file mode 100644 index 0000000000000..a57c6d9b0cbf9 --- /dev/null +++ b/tests/ui/auto-traits/issue-23080-2.next.stderr @@ -0,0 +1,11 @@ +error[E0380]: auto traits cannot have associated items + --> $DIR/issue-23080-2.rs:8:10 + | +LL | unsafe auto trait Trait { + | ----- auto traits cannot have associated items +LL | type Output; + | -----^^^^^^- help: remove these associated items + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0380`. diff --git a/tests/ui/auto-traits/issue-23080-2.rs b/tests/ui/auto-traits/issue-23080-2.rs index cb4cf6de1efe9..882b8f3938428 100644 --- a/tests/ui/auto-traits/issue-23080-2.rs +++ b/tests/ui/auto-traits/issue-23080-2.rs @@ -1,3 +1,6 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + #![feature(auto_traits)] #![feature(negative_impls)] From aab7589c401817d00859f76424508fd11a66d974 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 2 Jun 2023 23:45:29 +0330 Subject: [PATCH 630/806] Fix string pattern matching in mir interpreter --- crates/hir-ty/src/chalk_ext.rs | 5 +++++ crates/hir-ty/src/consteval/tests.rs | 15 ++++++++----- crates/hir-ty/src/mir/eval.rs | 25 ++++++++++++++++----- crates/hir-ty/src/mir/eval/shim.rs | 16 ++++++++++++++ crates/hir-ty/src/mir/eval/tests.rs | 33 ++++++++++++++++++++++++++++ crates/hir-ty/src/mir/lower.rs | 2 +- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 3fb3de5ba8d30..a8071591adac9 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -22,6 +22,7 @@ use crate::{ pub trait TyExt { fn is_unit(&self) -> bool; fn is_integral(&self) -> bool; + fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; fn is_unknown(&self) -> bool; @@ -68,6 +69,10 @@ impl TyExt for Ty { ) } + fn is_scalar(&self) -> bool { + matches!(self.kind(Interner), TyKind::Scalar(_)) + } + fn is_floating_point(&self) -> bool { matches!( self.kind(Interner), diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 6a7d045648635..06fff08b7d35b 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -179,6 +179,7 @@ fn casts() { "#, 4, ); + check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); } #[test] @@ -1034,16 +1035,18 @@ fn pattern_matching_literal() { ); check_number( r#" - const fn f(x: &str) -> u8 { + const fn f(x: &str) -> i32 { match x { - "foo" => 1, - "bar" => 10, - _ => 100, + "f" => 1, + "foo" => 10, + "" => 100, + "bar" => 1000, + _ => 10000, } } - const GOAL: u8 = f("foo") + f("bar"); + const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4; "#, - 11, + 4321, ); } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 6d7701c9e8d4b..6778808d529db 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -17,6 +17,7 @@ use hir_expand::InFile; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; +use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; @@ -896,7 +897,7 @@ impl Evaluator<'_> { Owned(c) } } - Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: { let lc = self.eval_operand(lhs, locals)?; let rc = self.eval_operand(rhs, locals)?; let mut lc = lc.get(&self)?; @@ -905,10 +906,17 @@ impl Evaluator<'_> { while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); let size = if ty.kind(Interner) == &TyKind::Str { - let ns = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + if *op != BinOp::Eq { + never!("Only eq is builtin for `str`"); + } + let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]); + if ls != rs { + break 'binary_op Owned(vec![0]); + } lc = &lc[..self.ptr_size()]; rc = &rc[..self.ptr_size()]; - ns + ls } else { self.size_of_sized(&ty, locals, "operand of binary op")? }; @@ -1200,8 +1208,15 @@ impl Evaluator<'_> { CastKind::IntToInt | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { - // FIXME: handle signed cast - let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); + let current_ty = self.operand_ty(operand, locals)?; + let is_signed = match current_ty.kind(Interner) { + TyKind::Scalar(s) => match s { + chalk_ir::Scalar::Int(_) => true, + _ => false, + }, + _ => false, + }; + let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, is_signed); let dest_size = self.size_of_sized(target_ty, locals, "destination of int to int cast")?; Owned(current[0..dest_size].to_vec()) diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 77ee7b6b6e2a8..3b9ef03c369f4 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -238,6 +238,22 @@ impl Evaluator<'_> { _span: MirSpan, ) -> Result<()> { match as_str { + "memcmp" => { + let [ptr1, ptr2, size] = args else { + return Err(MirEvalError::TypeError("memcmp args are not provided")); + }; + let addr1 = Address::from_bytes(ptr1.get(self)?)?; + let addr2 = Address::from_bytes(ptr2.get(self)?)?; + let size = from_bytes!(usize, size.get(self)?); + let slice1 = self.read_memory(addr1, size)?; + let slice2 = self.read_memory(addr2, size)?; + let r: i128 = match slice1.cmp(slice2) { + cmp::Ordering::Less => -1, + cmp::Ordering::Equal => 0, + cmp::Ordering::Greater => 1, + }; + destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size]) + } "write" => { let [fd, ptr, len] = args else { return Err(MirEvalError::TypeError("libc::write args are not provided")); diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 453c93de8e765..8c097539eb5e6 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -228,6 +228,39 @@ fn main() { ); } +#[test] +fn memcmp() { + check_pass( + r#" +//- minicore: slice, coerce_unsized, index + +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +extern "C" { + fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; +} + +fn my_cmp(x: &[u8], y: &[u8]) -> i32 { + memcmp(x as *const u8, y as *const u8, x.len()) +} + +fn main() { + if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 { + should_not_reach(); + } + if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 { + should_not_reach(); + } + if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 { + should_not_reach(); + } +} + "#, + ); +} + #[test] fn unix_write_stdout() { check_pass_and_stdio( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 5ed95133356cd..ebd419983551c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -829,7 +829,7 @@ impl<'ctx> MirLowerCtx<'ctx> { op, BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } ); - lhs_ty.as_builtin().is_some() && rhs_ty.as_builtin().is_some() && (lhs_ty == rhs_ty || builtin_inequal_impls) + lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) }; if !is_builtin { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { From ca4d0d4c24207928e5f8528e6d920b5905640d0a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 25 Feb 2023 19:53:37 +0000 Subject: [PATCH 631/806] Separate AnonConst from ConstBlock in HIR. --- compiler/rustc_ast_lowering/src/expr.rs | 10 +++++--- compiler/rustc_ast_lowering/src/index.rs | 8 ++++++ compiler/rustc_hir/src/hir.rs | 21 ++++++++++++++-- compiler/rustc_hir/src/intravisit.rs | 10 +++++++- .../rustc_hir_analysis/src/check/region.rs | 2 +- .../src/collect/generics_of.rs | 25 ++++++++----------- .../rustc_hir_analysis/src/collect/type_of.rs | 11 ++++---- compiler/rustc_hir_pretty/src/lib.rs | 7 +++--- compiler/rustc_hir_typeck/src/expr.rs | 12 ++++----- .../drop_ranges/cfg_build.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 3 +-- compiler/rustc_middle/src/hir/map/mod.rs | 19 +++++++------- compiler/rustc_mir_build/src/build/mod.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 15 +++-------- .../rustc_mir_transform/src/check_unsafety.rs | 6 ++--- compiler/rustc_passes/src/check_const.rs | 5 ++++ compiler/rustc_passes/src/dead.rs | 11 ++++++++ compiler/rustc_passes/src/loops.rs | 10 +++++--- compiler/rustc_ty_utils/src/ty.rs | 1 + 19 files changed, 111 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5e0ab80c6ac9f..f52797c4f3f1d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -71,9 +71,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(anon_const) => { - let anon_const = self.lower_anon_const(anon_const); - hir::ExprKind::ConstBlock(anon_const) + ExprKind::ConstBlock(c) => { + let c = self.with_new_scopes(|this| hir::ConstBlock { + def_id: this.local_def_id(c.id), + hir_id: this.lower_node_id(c.id), + body: this.lower_const_body(c.value.span, Some(&c.value)), + }); + hir::ExprKind::ConstBlock(c) } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 2e66c81eb0d05..ce847906fb99a 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -223,6 +223,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_inline_const(&mut self, constant: &'hir ConstBlock) { + self.insert(DUMMY_SP, constant.hir_id, Node::ConstBlock(constant)); + + self.with_parent(constant.hir_id, |this| { + intravisit::walk_inline_const(this, constant); + }); + } + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e844731091761..70fc66947df99 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1675,6 +1675,14 @@ pub struct AnonConst { pub body: BodyId, } +/// An inline constant expression `const { something }`. +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub struct ConstBlock { + pub hir_id: HirId, + pub def_id: LocalDefId, + pub body: BodyId, +} + /// An expression. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { @@ -1922,7 +1930,7 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ExprKind<'hir> { /// Allow anonymous constants from an inline `const` block - ConstBlock(AnonConst), + ConstBlock(ConstBlock), /// An array (e.g., `[a, b, c, d]`). Array(&'hir [Expr<'hir>]), /// A function call. @@ -3641,6 +3649,7 @@ pub enum Node<'hir> { Variant(&'hir Variant<'hir>), Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), + ConstBlock(&'hir ConstBlock), Expr(&'hir Expr<'hir>), ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), @@ -3695,6 +3704,7 @@ impl<'hir> Node<'hir> { Node::TypeBinding(b) => Some(b.ident), Node::Param(..) | Node::AnonConst(..) + | Node::ConstBlock(..) | Node::Expr(..) | Node::Stmt(..) | Node::Block(..) @@ -3758,7 +3768,7 @@ impl<'hir> Node<'hir> { }) | Node::Expr(Expr { kind: - ExprKind::ConstBlock(AnonConst { body, .. }) + ExprKind::ConstBlock(ConstBlock { body, .. }) | ExprKind::Closure(Closure { body, .. }) | ExprKind::Repeat(_, ArrayLen::Body(AnonConst { body, .. })), .. @@ -3878,6 +3888,13 @@ impl<'hir> Node<'hir> { this } + /// Expect a [`Node::ConstBlock`] or panic. + #[track_caller] + pub fn expect_inline_const(self) -> &'hir ConstBlock { + let Node::ConstBlock(this) = self else { self.expect_failed("an inline constant") }; + this + } + /// Expect a [`Node::Expr`] or panic. #[track_caller] pub fn expect_expr(self) -> &'hir Expr<'hir> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index df0047d82e12e..f84c814bd9278 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -335,6 +335,9 @@ pub trait Visitor<'v>: Sized { fn visit_anon_const(&mut self, c: &'v AnonConst) { walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'v ConstBlock) { + walk_inline_const(self, c) + } fn visit_expr(&mut self, ex: &'v Expr<'v>) { walk_expr(self, ex) } @@ -679,13 +682,18 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo visitor.visit_nested_body(constant.body); } +pub fn walk_inline_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v ConstBlock) { + visitor.visit_id(constant.hir_id); + visitor.visit_nested_body(constant.body); +} + pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { visitor.visit_id(expression.hir_id); match expression.kind { ExprKind::Array(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } - ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), + ExprKind::ConstBlock(ref const_block) => visitor.visit_inline_const(const_block), ExprKind::Repeat(ref element, ref count) => { visitor.visit_expr(element); visitor.visit_array_length(count) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 6ab5556e951d9..5bd6fcb9612dd 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -392,7 +392,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. hir::ExprKind::Closure(&hir::Closure { body, .. }) - | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { + | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index ed60998ec8dcf..32a7e8af862fa 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -123,9 +123,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { { Some(parent_def_id.to_def_id()) } - Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { - Some(tcx.typeck_root_def_id(def_id.to_def_id())) - } // Exclude `GlobalAsm` here which cannot have generics. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { @@ -142,7 +139,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } } - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + Node::ConstBlock(_) + | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { Some(tcx.typeck_root_def_id(def_id.to_def_id())) } Node::Item(item) => match item.kind { @@ -339,17 +337,14 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } // provide junk type parameter defs for const blocks. - if let Node::AnonConst(_) = node { - let parent_node = tcx.hir().get_parent(hir_id); - if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { - params.push(ty::GenericParamDef { - index: next_index(), - name: Symbol::intern(""), - def_id: def_id.to_def_id(), - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, - }); - } + if let Node::ConstBlock(_) = node { + params.push(ty::GenericParamDef { + index: next_index(), + name: Symbol::intern(""), + def_id: def_id.to_def_id(), + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, + }); } let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index e6fadd6cf4a85..c2b837fcfa670 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -34,12 +34,6 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => { return tcx.typeck(def_id).node_type(e.hir_id) } - Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) - if anon_const.hir_id == hir_id => - { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - return substs.as_inline_const().ty() - } Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { @@ -487,6 +481,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder anon_const_type_of(tcx, def_id), + Node::ConstBlock(_) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + substs.as_inline_const().ty() + } + Node::GenericParam(param) => match ¶m.kind { GenericParamKind::Type { default: Some(ty), .. } | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d93e8efc1b59a..ced46fe426c47 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -84,6 +84,7 @@ impl<'a> State<'a> { Node::ImplItem(a) => self.print_impl_item(a), Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), + Node::ConstBlock(a) => self.print_inline_const(a), Node::Expr(a) => self.print_expr(a), Node::ExprField(a) => self.print_expr_field(&a), Node::Stmt(a) => self.print_stmt(a), @@ -1095,10 +1096,10 @@ impl<'a> State<'a> { self.end() } - fn print_expr_anon_const(&mut self, anon_const: &hir::AnonConst) { + fn print_inline_const(&mut self, constant: &hir::ConstBlock) { self.ibox(INDENT_UNIT); self.word_space("const"); - self.print_anon_const(anon_const); + self.ann.nested(self, Nested::Body(constant.body)); self.end() } @@ -1370,7 +1371,7 @@ impl<'a> State<'a> { self.print_expr_vec(exprs); } hir::ExprKind::ConstBlock(ref anon_const) => { - self.print_expr_anon_const(anon_const); + self.print_inline_const(anon_const); } hir::ExprKind::Repeat(element, ref count) => { self.print_expr_repeat(element, count); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5e10add013b05..3c5feb1ba513d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -348,9 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => { - self.check_expr_const_block(anon_const, expected, expr) - } + ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected, expr), ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -1368,20 +1366,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_const_block( &self, - anon_const: &'tcx hir::AnonConst, + block: &'tcx hir::ConstBlock, expected: Expectation<'tcx>, _expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let body = self.tcx.hir().body(anon_const.body); + let body = self.tcx.hir().body(block.body); // Create a new function context. - let def_id = anon_const.def_id; + let def_id = block.def_id; let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id); crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); - fcx.write_ty(anon_const.hir_id, ty); + fcx.write_ty(block.hir_id, ty); ty } diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index e4a62ec05aef1..786a8c28f998b 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -270,6 +270,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::Variant(..) | hir::Node::Field(..) | hir::Node::AnonConst(..) + | hir::Node::ConstBlock(..) | hir::Node::Stmt(..) | hir::Node::PathSegment(..) | hir::Node::Ty(..) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 05c9f154574b2..cbc012c55c3a2 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1191,7 +1191,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> fn should_encode_const(def_kind: DefKind) -> bool { match def_kind { - DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => true, + DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst => true, DefKind::Struct | DefKind::Union @@ -1210,7 +1210,6 @@ fn should_encode_const(def_kind: DefKind) -> bool { | DefKind::Closure | DefKind::Generator | DefKind::ConstParam - | DefKind::InlineConst | DefKind::AssocTy | DefKind::TyParam | DefKind::Trait diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index d1ddc8fc1fd2e..5f2eb890c4eba 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -44,6 +44,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> { } Node::AnonConst(constant) => Some((constant.def_id, constant.body)), + Node::ConstBlock(constant) => Some((constant.def_id, constant.body)), _ => None, } @@ -240,15 +241,8 @@ impl<'hir> Map<'hir> { None => bug!("constructor node without a constructor"), } } - Node::AnonConst(_) => { - let inline = match self.find_parent(hir_id) { - Some(Node::Expr(&Expr { - kind: ExprKind::ConstBlock(ref anon_const), .. - })) if anon_const.hir_id == hir_id => true, - _ => false, - }; - if inline { DefKind::InlineConst } else { DefKind::AnonConst } - } + Node::AnonConst(_) => DefKind::AnonConst, + Node::ConstBlock(_) => DefKind::InlineConst, Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure, @@ -1060,6 +1054,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => variant.span, Node::Field(field) => field.span, Node::AnonConst(constant) => self.body(constant.body).value.span, + Node::ConstBlock(constant) => self.body(constant.body).value.span, Node::Expr(expr) => expr.span, Node::ExprField(field) => field.span, Node::Stmt(stmt) => stmt.span, @@ -1289,6 +1284,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id)) } Some(Node::AnonConst(_)) => node_str("const"), + Some(Node::ConstBlock(_)) => node_str("const"), Some(Node::Expr(_)) => node_str("expr"), Some(Node::ExprField(_)) => node_str("expr field"), Some(Node::Stmt(_)) => node_str("stmt"), @@ -1434,6 +1430,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { intravisit::walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'hir ConstBlock) { + self.body_owners.push(c.def_id); + intravisit::walk_inline_const(self, c) + } + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 8f6a069a7db54..7f0c1d53f729a 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -556,7 +556,7 @@ fn construct_const<'a, 'tcx>( span, .. }) => (*span, ty.span), - Node::AnonConst(_) => { + Node::AnonConst(_) | Node::ConstBlock(_) => { let span = tcx.def_span(def); (span, span) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 1bbe7b45c1ed2..8eeb055ed82d0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -582,22 +582,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts inline const patterns. fn lower_inline_const( &mut self, - anon_const: &'tcx hir::AnonConst, + block: &'tcx hir::ConstBlock, id: hir::HirId, span: Span, ) -> PatKind<'tcx> { let tcx = self.tcx; - let def_id = anon_const.def_id; - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = match tcx.hir().get(hir_id) { - hir::Node::AnonConst(ac) => ac.body, - _ => span_bug!( - tcx.def_span(def_id.to_def_id()), - "from_inline_const can only process anonymous constants" - ), - }; + let def_id = block.def_id; + let body_id = block.body; let expr = &tcx.hir().body(body_id).value; - let ty = tcx.typeck(def_id).node_type(hir_id); + let ty = tcx.typeck(def_id).node_type(block.hir_id); // Special case inline consts that are just literals. This is solely // a performance optimization, as we could also just go through the regular diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 069514d8a3bf3..2f851cd1eb5f7 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -421,10 +421,8 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> { intravisit::walk_block(self, block); } - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - if matches!(self.tcx.def_kind(c.def_id), DefKind::InlineConst) { - self.visit_body(self.tcx.hir().body(c.body)) - } + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + self.visit_body(self.tcx.hir().body(c.body)) } fn visit_fn( diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 2357b0aadefbb..fc437c429fbeb 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -199,6 +199,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); } + fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) { + let kind = Some(hir::ConstContext::Const); + self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); + } + fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { let owner = self.tcx.hir().body_owner_def_id(body.id()); let kind = self.tcx.hir().body_const_context(owner); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 7812dcde44c83..d5ac1cd9ce335 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -500,6 +500,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = in_pat; } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + // When inline const blocks are used in pattern position, paths + // referenced by it should be considered as used. + let in_pat = mem::replace(&mut self.in_pat, false); + + self.live_symbols.insert(c.def_id); + intravisit::walk_inline_const(self, c); + + self.in_pat = in_pat; + } } fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 73cfe68e7f214..7c64df6a50ecf 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -24,7 +24,7 @@ enum Context { Closure(Span), AsyncClosure(Span), LabeledBlock, - AnonConst, + Constant, } #[derive(Copy, Clone)] @@ -53,7 +53,11 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) { - self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c)); + self.with_context(Constant, |v| intravisit::walk_anon_const(v, c)); + } + + fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) { + self.with_context(Constant, |v| intravisit::walk_inline_const(v, c)); } fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { @@ -192,7 +196,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { AsyncClosure(closure_span) => { self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name }); } - Normal | AnonConst => { + Normal | Constant => { self.sess.emit_err(OutsideLoop { span, name, is_break: name == "break" }); } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b367d81a2608e..fe2d1fba7fe6e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -186,6 +186,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { kind: hir::TraitItemKind::Const(..), .. }) | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) | hir::Node::ImplItem(hir::ImplItem { kind: From 4fbb43e70f7d4d35702f0b5b3069931123262b71 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 Jun 2023 19:56:46 +0000 Subject: [PATCH 632/806] No more TyCtxt::lazy_normalization --- compiler/rustc_hir_analysis/src/collect/generics_of.rs | 2 +- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +- compiler/rustc_hir_analysis/src/outlives/mod.rs | 3 ++- compiler/rustc_infer/src/infer/combine.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 9 --------- compiler/rustc_trait_selection/src/traits/project.rs | 4 +++- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index ed60998ec8dcf..c6481022d7507 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -50,7 +50,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // We do not allow generic parameters in anon consts if we are inside // of a const parameter type, e.g. `struct Foo` is not allowed. None - } else if tcx.lazy_normalization() { + } else if tcx.features().generic_const_exprs { let parent_node = tcx.hir().get_parent(hir_id); if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node && constant.hir_id == hir_id diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 70d950eddd8a7..dcb5790292879 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -463,7 +463,7 @@ pub(super) fn explicit_predicates_of<'tcx>( } } } else { - if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() { + if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = tcx.hir().get_parent_item(hir_id); diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index 2106d6ff07dcc..fdbb890ce3d47 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -20,7 +20,8 @@ pub fn provide(providers: &mut Providers) { fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] { let id = tcx.hir().local_def_id_to_hir_id(item_def_id); - if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization() + if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) + && tcx.features().generic_const_exprs { if tcx.hir().opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index b6b935de68c84..94a72e5a4bda6 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -227,7 +227,7 @@ impl<'tcx> InferCtxt<'tcx> { return self.unify_const_variable(vid, a, relation.param_env()); } (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) - if self.tcx.lazy_normalization() => + if self.tcx.features().generic_const_exprs => { relation.register_const_equate_obligation(a, b); return Ok(b); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e7335104e619f..bf6f21968d71b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1015,15 +1015,6 @@ impl<'tcx> TyCtxt<'tcx> { self.query_system.on_disk_cache.as_ref().map_or(Ok(0), |c| c.serialize(self, encoder)) } - /// If `true`, we should use lazy normalization for constants, otherwise - /// we still evaluate them eagerly. - #[inline] - pub fn lazy_normalization(self) -> bool { - let features = self.features(); - // Note: We only use lazy normalization for generic const expressions. - features.generic_const_exprs - } - #[inline] pub fn local_crate_exports_generics(self) -> bool { debug_assert!(self.sess.opts.share_generics()); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 65af0bb1c4e1f..563cc257e0349 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -672,7 +672,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { let tcx = self.selcx.tcx(); - if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) { + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { constant } else { let constant = constant.super_fold_with(self); From 2c1473ca7042ab42489e11f3a1b97d7ecd39aa49 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 Jun 2023 20:23:44 +0000 Subject: [PATCH 633/806] Normalize anon consts in new solver --- compiler/rustc_infer/src/infer/combine.rs | 2 +- .../src/solve/eval_ctxt.rs | 17 +++++ .../src/solve/project_goals.rs | 64 ++++++++++++++----- tests/ui/traits/new-solver/array-default.rs | 8 +++ .../new-solver/structural-resolve-field.rs | 26 +------- ...valuated-const-impl-trait-ref.fails.stderr | 18 ++++++ .../unevaluated-const-impl-trait-ref.rs | 22 +++++++ 7 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 tests/ui/traits/new-solver/array-default.rs create mode 100644 tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr create mode 100644 tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 94a72e5a4bda6..ed532aa2e8ba8 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -227,7 +227,7 @@ impl<'tcx> InferCtxt<'tcx> { return self.unify_const_variable(vid, a, relation.param_env()); } (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) - if self.tcx.features().generic_const_exprs => + if self.tcx.features().generic_const_exprs || self.tcx.trait_solver_next() => { relation.register_const_equate_obligation(a, b); return Ok(b); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index f91c672775301..a004e903b5ea5 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -772,4 +772,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } values } + + // Try to evaluate a const, or return `None` if the const is too generic. + // This doesn't mean the const isn't evaluatable, though, and should be treated + // as an ambiguity rather than no-solution. + pub(super) fn try_const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + use rustc_middle::mir::interpret::ErrorHandled; + match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) { + Ok(ct) => Some(ct), + Err(ErrorHandled::Reported(e)) => Some(self.tcx().const_error(ty, e.into())), + Err(ErrorHandled::TooGeneric) => None, + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 7d7dfa2c83776..99ed9ac7b6208 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -22,25 +22,55 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - match goal.predicate.projection_ty.kind(self.tcx()) { - ty::AliasKind::Projection => { - // To only compute normalization once for each projection we only - // normalize if the expected term is an unconstrained inference variable. - // - // E.g. for `::Assoc == u32` we recursively compute the goal - // `exists ::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver. - if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + let def_id = goal.predicate.def_id(); + match self.tcx().def_kind(def_id) { + DefKind::AssocTy | DefKind::AssocConst => { + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + // To only compute normalization once for each projection we only + // normalize if the expected term is an unconstrained inference variable. + // + // E.g. for `::Assoc == u32` we recursively compute the goal + // `exists ::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver. + if self.term_is_fully_unconstrained(goal) { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + ty::AssocItemContainer::ImplContainer => bug!("IATs not supported here yet"), } } - ty::AliasKind::Opaque => self.normalize_opaque_type(goal), - ty::AliasKind::Inherent => bug!("IATs not supported here yet"), + DefKind::AnonConst => self.normalize_anon_const(goal), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), + kind => bug!("uknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn normalize_anon_const( + &mut self, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new( + goal.predicate.projection_ty.def_id, + goal.predicate.projection_ty.substs, + ), + self.tcx() + .type_of(goal.predicate.projection_ty.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } } diff --git a/tests/ui/traits/new-solver/array-default.rs b/tests/ui/traits/new-solver/array-default.rs new file mode 100644 index 0000000000000..5077137b09b4b --- /dev/null +++ b/tests/ui/traits/new-solver/array-default.rs @@ -0,0 +1,8 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +fn has_default() where [(); N]: Default {} + +fn main() { + has_default::<1>(); +} diff --git a/tests/ui/traits/new-solver/structural-resolve-field.rs b/tests/ui/traits/new-solver/structural-resolve-field.rs index c492d927696bd..01899c9ad645f 100644 --- a/tests/ui/traits/new-solver/structural-resolve-field.rs +++ b/tests/ui/traits/new-solver/structural-resolve-field.rs @@ -1,35 +1,13 @@ // compile-flags: -Ztrait-solver=next // check-pass +#[derive(Default)] struct Foo { x: i32, } -impl MyDefault for Foo { - fn my_default() -> Self { - Self { - x: 0, - } - } -} - -trait MyDefault { - fn my_default() -> Self; -} - -impl MyDefault for [Foo; 0] { - fn my_default() -> Self { - [] - } -} -impl MyDefault for [Foo; 1] { - fn my_default() -> Self { - [Foo::my_default(); 1] - } -} - fn main() { - let mut xs = <[Foo; 1]>::my_default(); + let mut xs = <[Foo; 1]>::default(); xs[0].x = 1; (&mut xs[0]).x = 2; } diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr new file mode 100644 index 0000000000000..072ac32a5de97 --- /dev/null +++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `(): Trait<1>` is not satisfied + --> $DIR/unevaluated-const-impl-trait-ref.rs:20:13 + | +LL | needs::<1>(); + | ^ the trait `Trait<1>` is not implemented for `()` + | + = help: the following other types implement trait `Trait`: + <() as Trait<0>> + <() as Trait<2>> +note: required by a bound in `needs` + --> $DIR/unevaluated-const-impl-trait-ref.rs:10:38 + | +LL | fn needs() where (): Trait {} + | ^^^^^^^^ required by this bound in `needs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs new file mode 100644 index 0000000000000..26c595bc97428 --- /dev/null +++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next +// revisions: works fails +//[works] check-pass + +trait Trait {} + +impl Trait<{ 1 - 1 }> for () {} +impl Trait<{ 1 + 1 }> for () {} + +fn needs() where (): Trait {} + +#[cfg(works)] +fn main() { + needs::<0>(); + needs::<2>(); +} + +#[cfg(fails)] +fn main() { + needs::<1>(); + //[fails]~^ ERROR the trait bound `(): Trait<1>` is not satisfied +} From 8912015f71a7013ad343fd7e29c355234f3540ac Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 Jun 2023 20:26:31 +0000 Subject: [PATCH 634/806] No const equate in new solver --- .../rustc_trait_selection/src/solve/eval_ctxt.rs | 7 +++++-- compiler/rustc_trait_selection/src/solve/fulfill.rs | 13 ++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index a004e903b5ea5..bc93b9e99ad44 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -322,10 +322,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Ambiguous => { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - // FIXME: implement these predicates :) - ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => { + // FIXME: implement this predicate :) + ty::PredicateKind::ConstEvaluatable(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + ty::PredicateKind::ConstEquate(_, _) => { + bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active") + } ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4a403196c7e05..212327448c874 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -118,16 +118,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { TypeError::Sorts(expected_found), ) } - ty::PredicateKind::ConstEquate(a, b) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((a, b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::CodeConstEquateError( - expected_found, - TypeError::ConstMismatch(expected_found), - ) - } ty::PredicateKind::Clause(_) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::ObjectSafe(_) @@ -138,7 +128,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { SelectionError::Unimplemented, ) } - ty::PredicateKind::TypeWellFormedFromEnv(_) => { + ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(_) => { bug!("unexpected goal: {goal:?}") } }, From 84196f33710e2797a576ddc5241e418b7a4f1f2c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 2 Jun 2023 22:07:53 +0000 Subject: [PATCH 635/806] Elaborate comment, make sure we do normalizes-to hack eventually for IATs, don't partially support const projection for impls --- .../src/solve/project_goals.rs | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 99ed9ac7b6208..23601f668ff05 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -25,29 +25,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { - match self.tcx().associated_item(def_id).container { - ty::AssocItemContainer::TraitContainer => { - // To only compute normalization once for each projection we only - // normalize if the expected term is an unconstrained inference variable. - // - // E.g. for `::Assoc == u32` we recursively compute the goal - // `exists ::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver. - if self.term_is_fully_unconstrained(goal) { + // To only compute normalization once for each projection we only + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. + // + // E.g. for `::Assoc == u32` we recursively compute the goal + // `exists ::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. + if self.term_is_fully_unconstrained(goal) { + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + ty::AssocItemContainer::ImplContainer => { + bug!("IATs not supported here yet") } } - ty::AssocItemContainer::ImplContainer => bug!("IATs not supported here yet"), + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } DefKind::AnonConst => self.normalize_anon_const(goal), DefKind::OpaqueTy => self.normalize_opaque_type(goal), - kind => bug!("uknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), } } @@ -203,17 +213,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ); // Finally we construct the actual value of the associated type. - let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst); - let ty = tcx.type_of(assoc_def.item.def_id); - let term: ty::EarlyBinder> = if is_const { - let identity_substs = - ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id); - let did = assoc_def.item.def_id; - let kind = - ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); - ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) - } else { - ty.map_bound(|ty| ty.into()) + let term = match assoc_def.item.kind { + ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), + ty::AssocKind::Const => bug!("associated const projection is not supported yet"), + ty::AssocKind::Fn => unreachable!("we should never project to a fn"), }; ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) From b002c9ff11805e681d3f5cd9aa33f14d15543bd8 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 8 May 2023 06:49:50 +0800 Subject: [PATCH 636/806] remove type ascription feature gate --- compiler/rustc_ast_passes/src/feature_gate.rs | 35 ++----------------- compiler/rustc_feature/src/active.rs | 2 -- compiler/rustc_hir_typeck/src/cast.rs | 4 +-- ...ial-casts-featuring-type-ascription.stderr | 4 +-- 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 274f931e43f9d..a6fd3b5c94310 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd}; -use rustc_errors::{Applicability, StashKey}; +use rustc_errors::StashKey; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; use rustc_session::Session; @@ -375,43 +375,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { - if let ast::StmtKind::Semi(expr) = &stmt.kind - && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind - && let ast::ExprKind::Type(..) = lhs.kind - && self.sess.parse_sess.span_diagnostic.err_count() == 0 - && !self.features.type_ascription - && !lhs.span.allows_unstable(sym::type_ascription) - { - // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type - // ascription error, but the likely intention was to write a `let` statement. (#78907). - feature_err( - &self.sess.parse_sess, - sym::type_ascription, - lhs.span, - "type ascription is experimental", - ).span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "you might have meant to introduce a new binding", - "let ", - Applicability::MachineApplicable, - ).emit(); - } visit::walk_stmt(self, stmt); } fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { ast::ExprKind::Type(..) => { - if self.sess.parse_sess.span_diagnostic.err_count() == 0 { - // To avoid noise about type ascription in common syntax errors, - // only emit if it is the *only* error. - gate_feature_post!( - &self, - type_ascription, - e.span, - "type ascription is experimental" - ); - } else { + if self.sess.parse_sess.span_diagnostic.err_count() > 0 { // And if it isn't, cancel the early-pass warning. if let Some(err) = self .sess @@ -629,7 +599,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!(type_ascription, "type ascription is experimental"); visit::walk_crate(&mut visitor, krate); } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 4c53f9d8369fd..549662a72fa5e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -534,8 +534,6 @@ declare_features! ( (active, try_blocks, "1.29.0", Some(31436), None), /// Allows `impl Trait` to be used inside type aliases (RFC 2515). (active, type_alias_impl_trait, "1.38.0", Some(63063), None), - /// Allows the use of type ascription in expressions. - (active, type_ascription, "1.6.0", Some(23416), None), /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (active, type_changing_struct_update, "1.58.0", Some(86555), None), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 98c683f0200a3..7a1e830073a72 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -689,8 +689,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { let t_cast = self.cast_ty; let t_expr = self.expr_ty; - let type_asc_or = - if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) } else { @@ -711,7 +709,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { |lint| { lint.help(format!( "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" + require a temporary variable" )) }, ); diff --git a/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr b/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr index 5087807b6c787..159a54873cc70 100644 --- a/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr +++ b/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr @@ -4,7 +4,7 @@ error: trivial numeric cast: `i32` as `i32` LL | let lugubrious = 12i32 as i32; | ^^^^^^^^^^^^ | - = help: cast can be replaced by coercion; this might require type ascription or a temporary variable + = help: cast can be replaced by coercion; this might require a temporary variable note: the lint level is defined here --> $DIR/trivial-casts-featuring-type-ascription.rs:1:24 | @@ -17,7 +17,7 @@ error: trivial cast: `&u32` as `*const u32` LL | let _ = haunted as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: cast can be replaced by coercion; this might require type ascription or a temporary variable + = help: cast can be replaced by coercion; this might require a temporary variable note: the lint level is defined here --> $DIR/trivial-casts-featuring-type-ascription.rs:1:9 | From 2d27932b6c935b1678af3fc76c8e40ac266b4b41 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 8 May 2023 21:00:26 +0800 Subject: [PATCH 637/806] remove EarlySyntaxWarning for type ascription --- compiler/rustc_ast_passes/src/feature_gate.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a6fd3b5c94310..99e2340e8d8b9 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -2,7 +2,6 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd}; -use rustc_errors::StashKey; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; use rustc_session::Session; @@ -380,19 +379,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { - ast::ExprKind::Type(..) => { - if self.sess.parse_sess.span_diagnostic.err_count() > 0 { - // And if it isn't, cancel the early-pass warning. - if let Some(err) = self - .sess - .parse_sess - .span_diagnostic - .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning) - { - err.cancel() - } - } - } ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } From 8baa32ff95539ee80136a662a0305d0c8dab9800 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 8 May 2023 23:35:04 +0800 Subject: [PATCH 638/806] cleanup --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 ---- compiler/rustc_feature/src/active.rs | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 99e2340e8d8b9..2125349909ef1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -373,10 +373,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { - visit::walk_stmt(self, stmt); - } - fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { ast::ExprKind::TryBlock(_) => { diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 549662a72fa5e..4c53f9d8369fd 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -534,6 +534,8 @@ declare_features! ( (active, try_blocks, "1.29.0", Some(31436), None), /// Allows `impl Trait` to be used inside type aliases (RFC 2515). (active, type_alias_impl_trait, "1.38.0", Some(63063), None), + /// Allows the use of type ascription in expressions. + (active, type_ascription, "1.6.0", Some(23416), None), /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (active, type_changing_struct_update, "1.58.0", Some(86555), None), From bff5ecd382f60f01255a4171363f11e2b206b4e8 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 3 Jun 2023 11:24:19 +0800 Subject: [PATCH 639/806] only check when we specify rustc in config.toml --- src/bootstrap/Cargo.lock | 7 +++++++ src/bootstrap/Cargo.toml | 1 + src/bootstrap/config.rs | 45 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 311ac1751606e..8f8778efee796 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -58,6 +58,7 @@ dependencies = [ "once_cell", "opener", "pretty_assertions", + "semver", "serde", "serde_derive", "serde_json", @@ -645,6 +646,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.160" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 70ade776d7da7..367c6190967c6 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -57,6 +57,7 @@ walkdir = "2" sysinfo = { version = "0.26.0", optional = true } clap = { version = "4.2.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } clap_complete = "4.2.2" +semver = "1.0.17" # Solaris doesn't support flock() and thus fd-lock is not option now [target.'cfg(not(target_os = "solaris"))'.dependencies] diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 45ad1547eb771..8ea7e83637596 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -25,6 +25,7 @@ use crate::flags::{Color, Flags, Warnings}; use crate::util::{exe, output, t}; use build_helper::detail_exit_macro; use once_cell::sync::OnceCell; +use semver::Version; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; @@ -1114,10 +1115,14 @@ impl Config { config.out = crate::util::absolute(&config.out); } - config.initial_rustc = build.rustc.map(PathBuf::from).unwrap_or_else(|| { + config.initial_rustc = if let Some(rustc) = build.rustc { + config.check_build_rustc_version(&rustc); + PathBuf::from(rustc) + } else { config.download_beta_toolchain(); config.out.join(config.build.triple).join("stage0/bin/rustc") - }); + }; + config.initial_cargo = build .cargo .map(|cargo| { @@ -1779,6 +1784,42 @@ impl Config { self.rust_codegen_backends.get(0).cloned() } + pub fn check_build_rustc_version(&self, rustc_path: &str) { + if self.dry_run() { + return; + } + + // check rustc version is same or lower with 1 apart from the building one + let mut cmd = Command::new(rustc_path); + cmd.arg("--version"); + let rustc_output = output(&mut cmd) + .lines() + .next() + .unwrap() + .split(' ') + .nth(1) + .unwrap() + .split('-') + .next() + .unwrap() + .to_owned(); + let rustc_version = Version::parse(&rustc_output.trim()).unwrap(); + let source_version = + Version::parse(&fs::read_to_string(self.src.join("src/version")).unwrap().trim()) + .unwrap(); + if !(source_version == rustc_version + || (source_version.major == rustc_version.major + && source_version.minor == rustc_version.minor + 1)) + { + let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1); + eprintln!( + "Unexpected rustc version: {}, we should use {}/{} to build source with {}", + rustc_version, prev_version, source_version, source_version + ); + detail_exit_macro!(1); + } + } + /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. fn download_ci_rustc_commit(&self, download_rustc: Option) -> Option { // If `download-rustc` is not set, default to rebuilding. From 2a7c6a99ef5c43311a430a07bd4eeab96a7c5d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Fri, 2 Jun 2023 14:40:44 +0800 Subject: [PATCH 640/806] Fix suggestion for matching struct with `..` on both ends --- compiler/rustc_parse/src/parser/pat.rs | 57 ++++++++++++++++++++------ tests/ui/parser/issue-112188.fixed | 14 +++++++ tests/ui/parser/issue-112188.rs | 14 +++++++ tests/ui/parser/issue-112188.stderr | 37 +++++++++++++++++ tests/ui/parser/issue-49257.stderr | 2 +- 5 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 tests/ui/parser/issue-112188.fixed create mode 100644 tests/ui/parser/issue-112188.rs create mode 100644 tests/ui/parser/issue-112188.stderr diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c317d96368ee6..fdf365178474d 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -938,7 +938,8 @@ impl<'a> Parser<'a> { let mut etc = false; let mut ate_comma = true; let mut delayed_err: Option> = None; - let mut etc_span = None; + let mut first_etc_and_maybe_comma_span = None; + let mut last_non_comma_dotdot_span = None; while self.token != token::CloseDelim(Delimiter::Brace) { let attrs = match self.parse_outer_attributes() { @@ -969,12 +970,27 @@ impl<'a> Parser<'a> { { etc = true; let mut etc_sp = self.token.span; + if first_etc_and_maybe_comma_span.is_none() { + if let Some(comma_tok) = self + .look_ahead(1, |t| if *t == token::Comma { Some(t.clone()) } else { None }) + { + let nw_span = self + .sess + .source_map() + .span_extend_to_line(comma_tok.span) + .trim_start(comma_tok.span.shrink_to_lo()) + .map(|s| self.sess.source_map().span_until_non_whitespace(s)); + first_etc_and_maybe_comma_span = nw_span.map(|s| etc_sp.to(s)); + } else { + first_etc_and_maybe_comma_span = + Some(self.sess.source_map().span_until_non_whitespace(etc_sp)); + } + } self.recover_bad_dot_dot(); self.bump(); // `..` || `...` || `_` if self.token == token::CloseDelim(Delimiter::Brace) { - etc_span = Some(etc_sp); break; } let token_str = super::token_descr(&self.token); @@ -996,7 +1012,6 @@ impl<'a> Parser<'a> { ate_comma = true; } - etc_span = Some(etc_sp.until(self.token.span)); if self.token == token::CloseDelim(Delimiter::Brace) { // If the struct looks otherwise well formed, recover and continue. if let Some(sp) = comma_sp { @@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> { } }?; ate_comma = this.eat(&token::Comma); + + last_non_comma_dotdot_span = Some(this.prev_token.span); + // We just ate a comma, so there's no need to use // `TrailingToken::Comma` Ok((field, TrailingToken::None)) @@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> { } if let Some(mut err) = delayed_err { - if let Some(etc_span) = etc_span { - err.multipart_suggestion( - "move the `..` to the end of the field list", - vec![ - (etc_span, String::new()), - (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), - ], - Applicability::MachineApplicable, - ); + if let Some(first_etc_span) = first_etc_and_maybe_comma_span { + if self.prev_token == token::DotDot { + // We have `.., x, ..`. + err.multipart_suggestion( + "remove the starting `..`", + vec![(first_etc_span, String::new())], + Applicability::MachineApplicable, + ); + } else { + if let Some(last_non_comma_dotdot_span) = last_non_comma_dotdot_span { + // We have `.., x`. + err.multipart_suggestion( + "move the `..` to the end of the field list", + vec![ + (first_etc_span, String::new()), + ( + self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()), + format!("{} .. }}", if ate_comma { "" } else { "," }), + ), + ], + Applicability::MachineApplicable, + ); + } + } } err.emit(); } diff --git a/tests/ui/parser/issue-112188.fixed b/tests/ui/parser/issue-112188.fixed new file mode 100644 index 0000000000000..5e73d8e38de81 --- /dev/null +++ b/tests/ui/parser/issue-112188.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused)] + +struct Foo { x: i32 } + +fn main() { + let f = Foo { x: 0 }; + let Foo { .. } = f; + let Foo { .. } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; + let Foo { x, .. } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; //~ ERROR expected `}`, found `,` +} diff --git a/tests/ui/parser/issue-112188.rs b/tests/ui/parser/issue-112188.rs new file mode 100644 index 0000000000000..27ca192e52263 --- /dev/null +++ b/tests/ui/parser/issue-112188.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused)] + +struct Foo { x: i32 } + +fn main() { + let f = Foo { x: 0 }; + let Foo { .. } = f; + let Foo { .., } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; + let Foo { .., x } = f; //~ ERROR expected `}`, found `,` + let Foo { .., x, .. } = f; //~ ERROR expected `}`, found `,` +} diff --git a/tests/ui/parser/issue-112188.stderr b/tests/ui/parser/issue-112188.stderr new file mode 100644 index 0000000000000..6d2d8e6a3b055 --- /dev/null +++ b/tests/ui/parser/issue-112188.stderr @@ -0,0 +1,37 @@ +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:10:17 + | +LL | let Foo { .., } = f; + | --^ + | | | + | | expected `}` + | | help: remove this comma + | `..` must be at the end and cannot have a trailing comma + +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:12:17 + | +LL | let Foo { .., x } = f; + | --^ + | | | + | | expected `}` + | `..` must be at the end and cannot have a trailing comma + | +help: move the `..` to the end of the field list + | +LL - let Foo { .., x } = f; +LL + let Foo { x, .. } = f; + | + +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:13:17 + | +LL | let Foo { .., x, .. } = f; + | --^- + | | | + | | expected `}` + | `..` must be at the end and cannot have a trailing comma + | help: remove the starting `..` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/issue-49257.stderr b/tests/ui/parser/issue-49257.stderr index 846467f7f2268..97e16f88b8d32 100644 --- a/tests/ui/parser/issue-49257.stderr +++ b/tests/ui/parser/issue-49257.stderr @@ -25,7 +25,7 @@ LL | let Point { .., y } = p; help: move the `..` to the end of the field list | LL - let Point { .., y } = p; -LL + let Point { y , .. } = p; +LL + let Point { y, .. } = p; | error: expected `}`, found `,` From f9c1a7dcd976bac5b357266d273c271d457c41b0 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sat, 3 Jun 2023 18:40:25 +0900 Subject: [PATCH 641/806] fix: assignment operators are right associative --- crates/parser/src/grammar/expressions.rs | 89 +++--- .../ok/0028_operator_binding_power.rast | 269 ++++++++++++++++++ .../parser/ok/0028_operator_binding_power.rs | 13 + .../ok/0072_destructuring_assignment.rast | 76 ++--- .../ok/0072_destructuring_assignment.rs | 2 +- 5 files changed, 374 insertions(+), 75 deletions(-) diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index e6fb9e9d335e8..1cbd1663230bd 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -4,8 +4,8 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST; use super::*; -pub(crate) use self::atom::{block_expr, match_arm_list}; -pub(super) use self::atom::{literal, LITERAL_FIRST}; +pub(crate) use atom::{block_expr, match_arm_list}; +pub(super) use atom::{literal, LITERAL_FIRST}; #[derive(PartialEq, Eq)] pub(super) enum Semicolon { @@ -188,47 +188,56 @@ struct Restrictions { prefer_stmt: bool, } +enum Associativity { + Left, + Right, +} + /// Binding powers of operators for a Pratt parser. /// /// See +/// +/// Note that Rust doesn't define associativity for some infix operators (e.g. `==` and `..`) and +/// requires parentheses to disambiguate. We just treat them as left associative. #[rustfmt::skip] -fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) { - const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); +fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) { + use Associativity::*; + const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left); match p.current() { - T![|] if p.at(T![||]) => (3, T![||]), - T![|] if p.at(T![|=]) => (1, T![|=]), - T![|] => (6, T![|]), - T![>] if p.at(T![>>=]) => (1, T![>>=]), - T![>] if p.at(T![>>]) => (9, T![>>]), - T![>] if p.at(T![>=]) => (5, T![>=]), - T![>] => (5, T![>]), + T![|] if p.at(T![||]) => (3, T![||], Left), + T![|] if p.at(T![|=]) => (1, T![|=], Right), + T![|] => (6, T![|], Left), + T![>] if p.at(T![>>=]) => (1, T![>>=], Right), + T![>] if p.at(T![>>]) => (9, T![>>], Left), + T![>] if p.at(T![>=]) => (5, T![>=], Left), + T![>] => (5, T![>], Left), T![=] if p.at(T![=>]) => NOT_AN_OP, - T![=] if p.at(T![==]) => (5, T![==]), - T![=] => (1, T![=]), - T![<] if p.at(T![<=]) => (5, T![<=]), - T![<] if p.at(T![<<=]) => (1, T![<<=]), - T![<] if p.at(T![<<]) => (9, T![<<]), - T![<] => (5, T![<]), - T![+] if p.at(T![+=]) => (1, T![+=]), - T![+] => (10, T![+]), - T![^] if p.at(T![^=]) => (1, T![^=]), - T![^] => (7, T![^]), - T![%] if p.at(T![%=]) => (1, T![%=]), - T![%] => (11, T![%]), - T![&] if p.at(T![&=]) => (1, T![&=]), + T![=] if p.at(T![==]) => (5, T![==], Left), + T![=] => (1, T![=], Right), + T![<] if p.at(T![<=]) => (5, T![<=], Left), + T![<] if p.at(T![<<=]) => (1, T![<<=], Right), + T![<] if p.at(T![<<]) => (9, T![<<], Left), + T![<] => (5, T![<], Left), + T![+] if p.at(T![+=]) => (1, T![+=], Right), + T![+] => (10, T![+], Left), + T![^] if p.at(T![^=]) => (1, T![^=], Right), + T![^] => (7, T![^], Left), + T![%] if p.at(T![%=]) => (1, T![%=], Right), + T![%] => (11, T![%], Left), + T![&] if p.at(T![&=]) => (1, T![&=], Right), // If you update this, remember to update `expr_let()` too. - T![&] if p.at(T![&&]) => (4, T![&&]), - T![&] => (8, T![&]), - T![/] if p.at(T![/=]) => (1, T![/=]), - T![/] => (11, T![/]), - T![*] if p.at(T![*=]) => (1, T![*=]), - T![*] => (11, T![*]), - T![.] if p.at(T![..=]) => (2, T![..=]), - T![.] if p.at(T![..]) => (2, T![..]), - T![!] if p.at(T![!=]) => (5, T![!=]), - T![-] if p.at(T![-=]) => (1, T![-=]), - T![-] => (10, T![-]), - T![as] => (12, T![as]), + T![&] if p.at(T![&&]) => (4, T![&&], Left), + T![&] => (8, T![&], Left), + T![/] if p.at(T![/=]) => (1, T![/=], Right), + T![/] => (11, T![/], Left), + T![*] if p.at(T![*=]) => (1, T![*=], Right), + T![*] => (11, T![*], Left), + T![.] if p.at(T![..=]) => (2, T![..=], Left), + T![.] if p.at(T![..]) => (2, T![..], Left), + T![!] if p.at(T![!=]) => (5, T![!=], Left), + T![-] if p.at(T![-=]) => (1, T![-=], Right), + T![-] => (10, T![-], Left), + T![as] => (12, T![as], Left), _ => NOT_AN_OP } @@ -273,7 +282,7 @@ fn expr_bp( loop { let is_range = p.at(T![..]) || p.at(T![..=]); - let (op_bp, op) = current_op(p); + let (op_bp, op, associativity) = current_op(p); if op_bp < bp { break; } @@ -306,7 +315,11 @@ fn expr_bp( } } - expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1); + let op_bp = match associativity { + Associativity::Left => op_bp + 1, + Associativity::Right => op_bp, + }; + expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp); lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); } Some((lhs, BlockLike::NotBlock)) diff --git a/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast b/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast index ae08c0756aa3a..43802572888f0 100644 --- a/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast +++ b/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast @@ -183,4 +183,273 @@ SOURCE_FILE COMMENT "//---&*1 - --2 * 9;" WHITESPACE "\n" R_CURLY "}" + WHITESPACE "\n\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "right_associative" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + PLUSEQ "+=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + MINUSEQ "-=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + STAREQ "*=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + SLASHEQ "/=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + PERCENTEQ "%=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + AMPEQ "&=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + PIPEEQ "|=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + CARETEQ "^=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + SHLEQ "<<=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + SHREQ ">>=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "mixed_associativity" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + COMMENT "// (a + b) = (c += ((d * e) = f))" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + PLUSEQ "+=" + WHITESPACE " " + BIN_EXPR + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + STAR "*" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs b/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs index cc9598470d84c..7ee3013a0c89e 100644 --- a/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs +++ b/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs @@ -12,3 +12,16 @@ fn binding_power() { //1 = 2 .. 3; //---&*1 - --2 * 9; } + +fn right_associative() { + a = b = c; + a = b += c -= d; + a = b *= c /= d %= e; + a = b &= c |= d ^= e; + a = b <<= c >>= d; +} + +fn mixed_associativity() { + // (a + b) = (c += ((d * e) = f)) + a + b = c += d * e = f; +} diff --git a/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast b/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast index e8b836dfbd09e..ce75c55189a0c 100644 --- a/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast +++ b/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast @@ -168,42 +168,46 @@ SOURCE_FILE WHITESPACE "\n " EXPR_STMT BIN_EXPR - BIN_EXPR - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Some" - ARG_LIST - L_PAREN "(" - RANGE_EXPR - DOT2 ".." - R_PAREN ")" - WHITESPACE " " - EQ "=" - WHITESPACE " " - METHOD_CALL_EXPR - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Some" - ARG_LIST - L_PAREN "(" - LITERAL - INT_NUMBER "0" - R_PAREN ")" - DOT "." - WHITESPACE "\n " - NAME_REF - IDENT "Ok" - ARG_LIST - L_PAREN "(" - UNDERSCORE_EXPR - UNDERSCORE "_" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + RANGE_EXPR + DOT2 ".." + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "0" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Ok" + ARG_LIST + L_PAREN "(" + UNDERSCORE_EXPR + UNDERSCORE "_" + R_PAREN ")" WHITESPACE " " EQ "=" WHITESPACE " " diff --git a/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs b/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs index 9d3e86603f881..d223b11f239e1 100644 --- a/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs +++ b/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs @@ -4,7 +4,7 @@ fn foo() { (_) = ..; struct S { a: i32 } S { .. } = S { ..S::default() }; - Some(..) = Some(0). + Some(..) = Some(0); Ok(_) = 0; let (a, b); [a, .., b] = [1, .., 2]; From 9e683442a9ed4d5c353f40d58dc64a0bc6492a59 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 3 Jun 2023 12:23:32 +0000 Subject: [PATCH 642/806] Only check inlining counter after recusing. --- compiler/rustc_mir_transform/src/inline.rs | 9 +- tests/mir-opt/inline/cycle.main.Inline.diff | 15 +- .../exponential_runtime.main.Inline.diff | 106 +++++++++++++- .../inline/inline_cycle.two.Inline.diff | 15 +- .../inline_cycle_generic.main.Inline.diff | 8 +- .../inline/inline_diverging.h.Inline.diff | 39 +---- ...ecked_ops.checked_shl.PreCodegen.after.mir | 50 +++---- .../loops.int_range.PreCodegen.after.mir | 96 ++++++------ ...nge_iter.forward_loop.PreCodegen.after.mir | 122 ++++++++-------- ..._iter.range_iter_next.PreCodegen.after.mir | 62 ++++---- ...slice_iter.range_loop.PreCodegen.after.mir | 138 +++++++++--------- 11 files changed, 392 insertions(+), 268 deletions(-) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 55b9f084c39f7..5487b5987e001 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -145,13 +145,16 @@ impl<'tcx> Inliner<'tcx> { Ok(new_blocks) => { debug!("inlined {}", callsite.callee); self.changed = true; + + self.history.push(callsite.callee.def_id()); + self.process_blocks(caller_body, new_blocks); + self.history.pop(); + inlined_count += 1; if inlined_count == inline_limit { + debug!("inline count reached"); return; } - self.history.push(callsite.callee.def_id()); - self.process_blocks(caller_body, new_blocks); - self.history.pop(); } } } diff --git a/tests/mir-opt/inline/cycle.main.Inline.diff b/tests/mir-opt/inline/cycle.main.Inline.diff index fdf6337a9bee6..7a39aae1baf5d 100644 --- a/tests/mir-opt/inline/cycle.main.Inline.diff +++ b/tests/mir-opt/inline/cycle.main.Inline.diff @@ -11,6 +11,14 @@ + let mut _3: &fn() {g}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6 + let _4: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8 + scope 2 (inlined >::call - shim(fn() {g})) { // at $DIR/cycle.rs:6:5: 6:8 ++ scope 3 (inlined g) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ scope 4 (inlined f::) { // at $DIR/cycle.rs:12:5: 12:12 ++ debug g => main; // in scope 4 at $DIR/cycle.rs:5:6: 5:7 ++ let _6: (); // in scope 4 at $DIR/cycle.rs:6:5: 6:8 ++ scope 5 (inlined >::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8 ++ } ++ } ++ } + } + } @@ -30,7 +38,11 @@ + _3 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6 + StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8 + _5 = const (); // scope 1 at $DIR/cycle.rs:6:5: 6:8 -+ _4 = move (*_3)() -> [return: bb4, unwind: bb2]; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ StorageLive(_6); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ _6 = main() -> [return: bb4, unwind: bb2]; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ // mir::Constant ++ // + span: no-location ++ // + literal: Const { ty: fn() {main}, val: Value() } } bb1: { @@ -50,6 +62,7 @@ + } + + bb4: { ++ StorageDead(_6); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL + StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8 + StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:7: 6:8 + drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2 diff --git a/tests/mir-opt/inline/exponential_runtime.main.Inline.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.diff index 30af8661dec73..7fad6aba82ec1 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.diff @@ -12,6 +12,26 @@ + let _5: (); // in scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 + let _6: (); // in scope 2 at $DIR/exponential_runtime.rs:63:9: 63:25 + let _7: (); // in scope 2 at $DIR/exponential_runtime.rs:64:9: 64:25 ++ scope 3 (inlined <() as E>::call) { // at $DIR/exponential_runtime.rs:62:9: 62:25 ++ let _8: (); // in scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ let _9: (); // in scope 3 at $DIR/exponential_runtime.rs:51:9: 51:25 ++ let _10: (); // in scope 3 at $DIR/exponential_runtime.rs:52:9: 52:25 ++ scope 4 (inlined <() as D>::call) { // at $DIR/exponential_runtime.rs:50:9: 50:25 ++ let _11: (); // in scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ let _12: (); // in scope 4 at $DIR/exponential_runtime.rs:39:9: 39:25 ++ let _13: (); // in scope 4 at $DIR/exponential_runtime.rs:40:9: 40:25 ++ scope 5 (inlined <() as C>::call) { // at $DIR/exponential_runtime.rs:38:9: 38:25 ++ let _14: (); // in scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ let _15: (); // in scope 5 at $DIR/exponential_runtime.rs:27:9: 27:25 ++ let _16: (); // in scope 5 at $DIR/exponential_runtime.rs:28:9: 28:25 ++ scope 6 (inlined <() as B>::call) { // at $DIR/exponential_runtime.rs:26:9: 26:25 ++ let _17: (); // in scope 6 at $DIR/exponential_runtime.rs:14:9: 14:25 ++ let _18: (); // in scope 6 at $DIR/exponential_runtime.rs:15:9: 15:25 ++ let _19: (); // in scope 6 at $DIR/exponential_runtime.rs:16:9: 16:25 ++ } ++ } ++ } ++ } + } + } @@ -24,12 +44,24 @@ + StorageLive(_5); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 + StorageLive(_6); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 + StorageLive(_7); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 -+ _5 = <() as E>::call() -> bb4; // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_8); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_9); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_10); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_11); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_12); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_13); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_14); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_15); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_16); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_17); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageLive(_18); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageLive(_19); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ _17 = <() as A>::call() -> bb12; // scope 6 at $DIR/exponential_runtime.rs:14:9: 14:25 // mir::Constant - // + span: $DIR/exponential_runtime.rs:87:5: 87:20 - // + literal: Const { ty: fn() {<() as G>::call}, val: Value() } -+ // + span: $DIR/exponential_runtime.rs:62:9: 62:23 -+ // + literal: Const { ty: fn() {<() as E>::call}, val: Value() } ++ // + span: $DIR/exponential_runtime.rs:14:9: 14:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } } bb1: { @@ -59,6 +91,9 @@ + } + + bb4: { ++ StorageDead(_10); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageDead(_9); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageDead(_8); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 + _6 = <() as E>::call() -> bb5; // scope 2 at $DIR/exponential_runtime.rs:63:9: 63:25 + // mir::Constant + // + span: $DIR/exponential_runtime.rs:63:9: 63:23 @@ -70,6 +105,71 @@ + // mir::Constant + // + span: $DIR/exponential_runtime.rs:64:9: 64:23 + // + literal: Const { ty: fn() {<() as E>::call}, val: Value() } ++ } ++ ++ bb6: { ++ StorageDead(_13); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageDead(_12); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageDead(_11); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ _9 = <() as D>::call() -> bb7; // scope 3 at $DIR/exponential_runtime.rs:51:9: 51:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:51:9: 51:23 ++ // + literal: Const { ty: fn() {<() as D>::call}, val: Value() } ++ } ++ ++ bb7: { ++ _10 = <() as D>::call() -> bb4; // scope 3 at $DIR/exponential_runtime.rs:52:9: 52:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:52:9: 52:23 ++ // + literal: Const { ty: fn() {<() as D>::call}, val: Value() } ++ } ++ ++ bb8: { ++ StorageDead(_16); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageDead(_15); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageDead(_14); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ _12 = <() as C>::call() -> bb9; // scope 4 at $DIR/exponential_runtime.rs:39:9: 39:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:39:9: 39:23 ++ // + literal: Const { ty: fn() {<() as C>::call}, val: Value() } ++ } ++ ++ bb9: { ++ _13 = <() as C>::call() -> bb6; // scope 4 at $DIR/exponential_runtime.rs:40:9: 40:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:40:9: 40:23 ++ // + literal: Const { ty: fn() {<() as C>::call}, val: Value() } ++ } ++ ++ bb10: { ++ StorageDead(_19); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageDead(_18); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageDead(_17); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ _15 = <() as B>::call() -> bb11; // scope 5 at $DIR/exponential_runtime.rs:27:9: 27:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:27:9: 27:23 ++ // + literal: Const { ty: fn() {<() as B>::call}, val: Value() } ++ } ++ ++ bb11: { ++ _16 = <() as B>::call() -> bb8; // scope 5 at $DIR/exponential_runtime.rs:28:9: 28:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:28:9: 28:23 ++ // + literal: Const { ty: fn() {<() as B>::call}, val: Value() } ++ } ++ ++ bb12: { ++ _18 = <() as A>::call() -> bb13; // scope 6 at $DIR/exponential_runtime.rs:15:9: 15:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:15:9: 15:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } ++ } ++ ++ bb13: { ++ _19 = <() as A>::call() -> bb10; // scope 6 at $DIR/exponential_runtime.rs:16:9: 16:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:16:9: 16:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } } } diff --git a/tests/mir-opt/inline/inline_cycle.two.Inline.diff b/tests/mir-opt/inline/inline_cycle.two.Inline.diff index c8f58111da7da..48f0bd1030183 100644 --- a/tests/mir-opt/inline/inline_cycle.two.Inline.diff +++ b/tests/mir-opt/inline/inline_cycle.two.Inline.diff @@ -10,6 +10,14 @@ + debug f => _2; // in scope 1 at $DIR/inline_cycle.rs:54:22: 54:23 + let _3: (); // in scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + scope 2 (inlined >::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:55:5: 55:8 ++ scope 3 (inlined f) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ scope 4 (inlined call::) { // at $DIR/inline_cycle.rs:60:5: 60:12 ++ debug f => f; // in scope 4 at $DIR/inline_cycle.rs:54:22: 54:23 ++ let _5: (); // in scope 4 at $DIR/inline_cycle.rs:55:5: 55:8 ++ scope 5 (inlined >::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:55:5: 55:8 ++ } ++ } ++ } + } + } @@ -27,10 +35,15 @@ + StorageLive(_3); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 + StorageLive(_4); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + _4 = const (); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 -+ _3 = move _2() -> bb1; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ StorageLive(_5); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ _5 = f() -> bb1; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ // mir::Constant ++ // + span: no-location ++ // + literal: Const { ty: fn() {f}, val: Value() } } bb1: { ++ StorageDead(_5); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL + StorageDead(_4); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + StorageDead(_3); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 + StorageDead(_2); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 diff --git a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff index 9429ca593646e..8696e624b2237 100644 --- a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff +++ b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff @@ -6,18 +6,20 @@ let _1: (); // in scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 + scope 1 (inlined ::call) { // at $DIR/inline_cycle_generic.rs:10:5: 10:24 + scope 2 (inlined as Call>::call) { // at $DIR/inline_cycle_generic.rs:39:9: 39:31 ++ scope 3 (inlined ::call) { // at $DIR/inline_cycle_generic.rs:32:9: 32:28 ++ } + } + } bb0: { StorageLive(_1); // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 - _1 = ::call() -> bb1; // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 -+ _1 = ::call() -> bb1; // scope 2 at $DIR/inline_cycle_generic.rs:32:9: 32:28 ++ _1 = as Call>::call() -> bb1; // scope 3 at $DIR/inline_cycle_generic.rs:24:9: 24:31 // mir::Constant - // + span: $DIR/inline_cycle_generic.rs:10:5: 10:22 - // + literal: Const { ty: fn() {::call}, val: Value() } -+ // + span: $DIR/inline_cycle_generic.rs:32:9: 32:26 -+ // + literal: Const { ty: fn() {::call}, val: Value() } ++ // + span: $DIR/inline_cycle_generic.rs:24:9: 24:29 ++ // + literal: Const { ty: fn() { as Call>::call}, val: Value() } } bb1: { diff --git a/tests/mir-opt/inline/inline_diverging.h.Inline.diff b/tests/mir-opt/inline/inline_diverging.h.Inline.diff index 255451e867030..78cd47c5f4bf4 100644 --- a/tests/mir-opt/inline/inline_diverging.h.Inline.diff +++ b/tests/mir-opt/inline/inline_diverging.h.Inline.diff @@ -20,6 +20,8 @@ + } + } + scope 4 (inlined ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline_diverging.rs:27:13: 27:16 ++ scope 5 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ } + } + } @@ -40,44 +42,11 @@ + _3 = &_2; // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14 + StorageLive(_8); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 + _8 = const (); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 -+ _4 = move (*_3)() -> [return: bb6, unwind: bb4]; // scope 4 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12 + } + + bb1: { -+ StorageDead(_5); // scope 2 at $DIR/inline_diverging.rs:28:15: 28:16 -+ StorageLive(_7); // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7 -+ _7 = move _4; // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7 -+ _1 = (move _7, move _6); // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11 -+ StorageDead(_7); // scope 3 at $DIR/inline_diverging.rs:29:10: 29:11 -+ StorageDead(_4); // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ drop(_2) -> bb2; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb2: { -+ unreachable; // scope 0 at $DIR/inline_diverging.rs:30:2: 30:2 -+ } -+ -+ bb3 (cleanup): { -+ drop(_4) -> [return: bb4, unwind terminate]; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb4 (cleanup): { -+ drop(_2) -> [return: bb5, unwind terminate]; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb5 (cleanup): { -+ resume; // scope 1 at $DIR/inline_diverging.rs:26:1: 30:2 -+ } -+ -+ bb6: { -+ StorageDead(_8); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 -+ StorageDead(_3); // scope 1 at $DIR/inline_diverging.rs:27:15: 27:16 -+ StorageLive(_5); // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14 -+ _5 = &_2; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14 -+ _6 = ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb1, unwind: bb3]; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:16 -+ // mir::Constant -+ // + span: $DIR/inline_diverging.rs:28:13: 28:14 -+ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() -> ! {sleep}, ()) -> ! {sleep} as FnOnce<()>>::Output { ! {sleep} as Fn<()>>::call}, val: Value() } ++ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12 } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir index 55945bbc8fcc0..dff3cbbe76d22 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir @@ -38,37 +38,37 @@ fn checked_shl(_1: u32, _2: u32) -> Option { debug self => _4; // in scope 10 at $SRC_DIR/core/src/convert/mod.rs:LL:COL scope 11 (inlined >::try_from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL debug value => _4; // in scope 11 at $SRC_DIR/core/src/convert/mod.rs:LL:COL - scope 21 (inlined >::into) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL - debug self => _4; // in scope 21 at $SRC_DIR/core/src/convert/mod.rs:LL:COL - scope 22 (inlined >::from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL - debug t => _4; // in scope 22 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + scope 12 (inlined >::into) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL + debug self => _4; // in scope 12 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + scope 13 (inlined >::from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL + debug t => _4; // in scope 13 at $SRC_DIR/core/src/convert/mod.rs:LL:COL } } } } - scope 12 (inlined Result::::ok) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL - debug self => _5; // in scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - let _6: u32; // in scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - scope 13 { - debug x => _6; // in scope 13 at $SRC_DIR/core/src/result.rs:LL:COL + scope 14 (inlined Result::::ok) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL + debug self => _5; // in scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + let _6: u32; // in scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + scope 15 { + debug x => _6; // in scope 15 at $SRC_DIR/core/src/result.rs:LL:COL } } - scope 14 (inlined #[track_caller] Option::::unwrap_unchecked) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL - debug self => _7; // in scope 14 at $SRC_DIR/core/src/option.rs:LL:COL - let mut _13: &std::option::Option; // in scope 14 at $SRC_DIR/core/src/option.rs:LL:COL - scope 15 { - debug val => _8; // in scope 15 at $SRC_DIR/core/src/option.rs:LL:COL + scope 16 (inlined #[track_caller] Option::::unwrap_unchecked) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL + debug self => _7; // in scope 16 at $SRC_DIR/core/src/option.rs:LL:COL + let mut _13: &std::option::Option; // in scope 16 at $SRC_DIR/core/src/option.rs:LL:COL + scope 17 { + debug val => _8; // in scope 17 at $SRC_DIR/core/src/option.rs:LL:COL } - scope 16 { - scope 18 (inlined unreachable_unchecked) { // at $SRC_DIR/core/src/option.rs:LL:COL - scope 19 { - scope 20 (inlined unreachable_unchecked::runtime) { // at $SRC_DIR/core/src/intrinsics.rs:LL:COL + scope 18 { + scope 20 (inlined unreachable_unchecked) { // at $SRC_DIR/core/src/option.rs:LL:COL + scope 21 { + scope 22 (inlined unreachable_unchecked::runtime) { // at $SRC_DIR/core/src/intrinsics.rs:LL:COL } } } } - scope 17 (inlined Option::::is_some) { // at $SRC_DIR/core/src/option.rs:LL:COL - debug self => _13; // in scope 17 at $SRC_DIR/core/src/option.rs:LL:COL + scope 19 (inlined Option::::is_some) { // at $SRC_DIR/core/src/option.rs:LL:COL + debug self => _13; // in scope 19 at $SRC_DIR/core/src/option.rs:LL:COL } } } @@ -84,7 +84,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { StorageLive(_10); // scope 0 at $DIR/checked_ops.rs:+1:7: +1:23 StorageLive(_11); // scope 0 at $DIR/checked_ops.rs:+1:7: +1:23 StorageLive(_9); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - StorageLive(_4); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + StorageLive(_4); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageLive(_3); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _3 = const 31_u32; // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _4 = BitAnd(_2, move _3); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL @@ -94,12 +94,12 @@ fn checked_shl(_1: u32, _2: u32) -> Option { StorageLive(_5); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL _5 = Result::::Ok(_4); // scope 11 at $SRC_DIR/core/src/convert/mod.rs:LL:COL StorageLive(_6); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL - _6 = move ((_5 as Ok).0: u32); // scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - _7 = Option::::Some(move _6); // scope 13 at $SRC_DIR/core/src/result.rs:LL:COL + _6 = move ((_5 as Ok).0: u32); // scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + _7 = Option::::Some(move _6); // scope 15 at $SRC_DIR/core/src/result.rs:LL:COL StorageDead(_6); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageDead(_5); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageLive(_13); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL - _8 = move ((_7 as Some).0: u32); // scope 14 at $SRC_DIR/core/src/option.rs:LL:COL + _8 = move ((_7 as Some).0: u32); // scope 16 at $SRC_DIR/core/src/option.rs:LL:COL StorageDead(_13); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageDead(_7); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL _9 = unchecked_shl::(_1, move _8) -> [return: bb1, unwind unreachable]; // scope 7 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL @@ -110,7 +110,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb1: { StorageDead(_8); // scope 7 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - StorageDead(_4); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + StorageDead(_4); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _10 = Ge(_2, const _); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _11 = move _9; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageDead(_9); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index a5002cd6afa13..86e0a62b6f968 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -7,14 +7,14 @@ fn int_range(_1: usize, _2: usize) -> () { let mut _3: std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 let mut _4: std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 let mut _5: &mut std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 - let mut _9: std::option::Option; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 - let mut _12: isize; // in scope 0 at $DIR/loops.rs:+1:5: +3:6 - let _14: (); // in scope 0 at $DIR/loops.rs:+1:14: +1:24 + let mut _11: std::option::Option; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 + let mut _14: isize; // in scope 0 at $DIR/loops.rs:+1:5: +3:6 + let _16: (); // in scope 0 at $DIR/loops.rs:+1:14: +1:24 scope 1 { debug iter => _4; // in scope 1 at $DIR/loops.rs:+1:14: +1:24 - let _13: usize; // in scope 1 at $DIR/loops.rs:+1:9: +1:10 + let _15: usize; // in scope 1 at $DIR/loops.rs:+1:9: +1:10 scope 2 { - debug i => _13; // in scope 2 at $DIR/loops.rs:+1:9: +1:10 + debug i => _15; // in scope 2 at $DIR/loops.rs:+1:9: +1:10 } scope 4 (inlined iter::range::>::next) { // at $DIR/loops.rs:8:14: 8:24 debug self => _5; // in scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL @@ -22,14 +22,20 @@ fn int_range(_1: usize, _2: usize) -> () { debug self => _5; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _6: &usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _8: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _10: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _11: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _10: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _12: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _13: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 6 { - debug old => _10; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _12; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { } } + scope 8 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _6; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _7; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _8: usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -45,74 +51,74 @@ fn int_range(_1: usize, _2: usize) -> () { } bb1: { - StorageLive(_9); // scope 1 at $DIR/loops.rs:+1:14: +1:24 + StorageLive(_11); // scope 1 at $DIR/loops.rs:+1:14: +1:24 _5 = &mut _4; // scope 1 at $DIR/loops.rs:+1:14: +1:24 - StorageLive(_10); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_12); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_10); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_6); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _6 = &((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_5).1: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _8 = ::lt(move _6, move _7) -> bb2; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a usize, &'b usize) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _8 = (*_6); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = Lt(move _8, move _9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_6); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _8) -> [0: bb3, otherwise: bb4]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _10) -> [0: bb2, otherwise: bb3]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _9 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _11 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _10 = ((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _11 = ::forward_unchecked(_10, const 1_usize) -> bb5; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _12 = ((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _13 = ::forward_unchecked(_12, const 1_usize) -> bb4; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(usize, usize) -> usize {::forward_unchecked}, val: Value() } } - bb5: { - ((*_5).0: usize) = move _11; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = Option::::Some(_10); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + ((*_5).0: usize) = move _13; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _11 = Option::::Some(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb6: { - StorageDead(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_10); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = discriminant(_9); // scope 1 at $DIR/loops.rs:+1:14: +1:24 - switchInt(move _12) -> [0: bb7, 1: bb8, otherwise: bb10]; // scope 1 at $DIR/loops.rs:+1:14: +1:24 + bb5: { + StorageDead(_10); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_12); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = discriminant(_11); // scope 1 at $DIR/loops.rs:+1:14: +1:24 + switchInt(move _14) -> [0: bb6, 1: bb7, otherwise: bb9]; // scope 1 at $DIR/loops.rs:+1:14: +1:24 } - bb7: { - StorageDead(_9); // scope 1 at $DIR/loops.rs:+3:5: +3:6 + bb6: { + StorageDead(_11); // scope 1 at $DIR/loops.rs:+3:5: +3:6 StorageDead(_4); // scope 0 at $DIR/loops.rs:+3:5: +3:6 return; // scope 0 at $DIR/loops.rs:+4:2: +4:2 } - bb8: { - _13 = ((_9 as Some).0: usize); // scope 1 at $DIR/loops.rs:+1:9: +1:10 - _14 = opaque::(_13) -> bb9; // scope 2 at $DIR/loops.rs:+2:9: +2:18 + bb7: { + _15 = ((_11 as Some).0: usize); // scope 1 at $DIR/loops.rs:+1:9: +1:10 + _16 = opaque::(_15) -> bb8; // scope 2 at $DIR/loops.rs:+2:9: +2:18 // mir::Constant // + span: $DIR/loops.rs:9:9: 9:15 // + literal: Const { ty: fn(usize) {opaque::}, val: Value() } } - bb9: { - StorageDead(_9); // scope 1 at $DIR/loops.rs:+3:5: +3:6 + bb8: { + StorageDead(_11); // scope 1 at $DIR/loops.rs:+3:5: +3:6 goto -> bb1; // scope 1 at $DIR/loops.rs:+1:5: +3:6 } - bb10: { + bb9: { unreachable; // scope 1 at $DIR/loops.rs:+1:14: +1:24 } } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir index 693939e75f4f4..06a4e35f1f900 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir @@ -8,16 +8,16 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { let mut _4: std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 let mut _5: std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 let mut _6: &mut std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 - let mut _10: std::option::Option; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 - let mut _13: isize; // in scope 0 at $DIR/range_iter.rs:+1:5: +3:6 - let mut _15: &impl Fn(u32); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:10 - let mut _16: (u32,); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:13 - let _17: (); // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 + let mut _12: std::option::Option; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 + let mut _15: isize; // in scope 0 at $DIR/range_iter.rs:+1:5: +3:6 + let mut _17: &impl Fn(u32); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:10 + let mut _18: (u32,); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:13 + let _19: (); // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 scope 1 { debug iter => _5; // in scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - let _14: u32; // in scope 1 at $DIR/range_iter.rs:+1:9: +1:10 + let _16: u32; // in scope 1 at $DIR/range_iter.rs:+1:9: +1:10 scope 2 { - debug x => _14; // in scope 2 at $DIR/range_iter.rs:+1:9: +1:10 + debug x => _16; // in scope 2 at $DIR/range_iter.rs:+1:9: +1:10 } scope 4 (inlined iter::range::>::next) { // at $DIR/range_iter.rs:21:14: 21:24 debug self => _6; // in scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL @@ -25,14 +25,20 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug self => _6; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _8: &u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _9: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _11: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _12: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _11: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _13: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _14: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 6 { - debug old => _11; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _13; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { } } + scope 8 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _7; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _8; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: u32; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _10: u32; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -48,92 +54,92 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb1: { - StorageLive(_10); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + StorageLive(_12); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 _6 = &mut _5; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - StorageLive(_11); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_9); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _8 = &((*_6).1: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = ::lt(move _7, move _8) -> [return: bb2, unwind: bb12]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a u32, &'b u32) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = (*_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _11 = Lt(move _9, move _10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _9) -> [0: bb3, otherwise: bb4]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _11) -> [0: bb2, otherwise: bb3]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _10 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _12 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _11 = ((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = ::forward_unchecked(_11, const 1_usize) -> [return: bb5, unwind: bb12]; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _13 = ((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_14); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = ::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb11]; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(u32, usize) -> u32 {::forward_unchecked}, val: Value() } } - bb5: { - ((*_6).0: u32) = move _12; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _10 = Option::::Some(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + ((*_6).0: u32) = move _14; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_14); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _12 = Option::::Some(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb6: { - StorageDead(_9); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _13 = discriminant(_10); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - switchInt(move _13) -> [0: bb7, 1: bb9, otherwise: bb11]; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + bb5: { + StorageDead(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _15 = discriminant(_12); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 } - bb7: { - StorageDead(_10); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 + bb6: { + StorageDead(_12); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 StorageDead(_5); // scope 0 at $DIR/range_iter.rs:+3:5: +3:6 - drop(_3) -> bb8; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 + drop(_3) -> bb7; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 } - bb8: { + bb7: { return; // scope 0 at $DIR/range_iter.rs:+4:2: +4:2 } - bb9: { - _14 = ((_10 as Some).0: u32); // scope 1 at $DIR/range_iter.rs:+1:9: +1:10 - StorageLive(_15); // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 - _15 = &_3; // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 - StorageLive(_16); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 - _16 = (_14,); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 - _17 = >::call(move _15, move _16) -> [return: bb10, unwind: bb12]; // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + bb8: { + _16 = ((_12 as Some).0: u32); // scope 1 at $DIR/range_iter.rs:+1:9: +1:10 + StorageLive(_17); // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 + _17 = &_3; // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 + StorageLive(_18); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + _18 = (_16,); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 // mir::Constant // + span: $DIR/range_iter.rs:22:9: 22:10 // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a impl Fn(u32), (u32,)) -> >::Output {>::call}, val: Value() } } - bb10: { - StorageDead(_16); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 - StorageDead(_15); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 - StorageDead(_10); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 + bb9: { + StorageDead(_18); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 + StorageDead(_17); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 + StorageDead(_12); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 goto -> bb1; // scope 1 at $DIR/range_iter.rs:+1:5: +3:6 } - bb11: { + bb10: { unreachable; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 } - bb12 (cleanup): { - drop(_3) -> [return: bb13, unwind terminate]; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 + bb11 (cleanup): { + drop(_3) -> [return: bb12, unwind terminate]; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 } - bb13 (cleanup): { + bb12 (cleanup): { resume; // scope 0 at $DIR/range_iter.rs:+0:1: +4:2 } } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir index 668ec31c6c134..f15722deee013 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir @@ -9,60 +9,66 @@ fn range_iter_next(_1: &mut std::ops::Range) -> Option { debug self => _1; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _2: &u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _3: &u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _4: bool; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _5: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _6: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _6: bool; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _7: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _8: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 3 { - debug old => _5; // in scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _7; // in scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 4 { } } + scope 5 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _2; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _3; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _4: u32; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _5: u32; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } bb0: { - StorageLive(_5); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_4); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_7); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_6); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_2); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL _2 = &((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_3); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL _3 = &((*_1).1: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _4 = ::lt(move _2, move _3) -> bb1; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a u32, &'b u32) -> bool {::lt}, val: Value() } - } - - bb1: { + StorageLive(_4); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _4 = (*_2); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _5 = (*_3); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _6 = Lt(move _4, move _5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_4); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_3); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_2); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _4) -> [0: bb2, otherwise: bb3]; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _6) -> [0: bb1, otherwise: bb2]; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb2: { + bb1: { _0 = Option::::None; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb5; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb4; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _5 = ((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_6); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _6 = ::forward_unchecked(_5, const 1_usize) -> bb4; // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _7 = ((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_8); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _8 = ::forward_unchecked(_7, const 1_usize) -> bb3; // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(u32, usize) -> u32 {::forward_unchecked}, val: Value() } } - bb4: { - ((*_1).0: u32) = move _6; // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_6); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _0 = Option::::Some(_5); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb5; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + ((*_1).0: u32) = move _8; // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_8); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _0 = Option::::Some(_7); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb4; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb5: { - StorageDead(_4); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_5); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + StorageDead(_6); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_7); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL return; // scope 0 at $DIR/range_iter.rs:+2:2: +2:2 } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir index ca7a4a64f4574..870496f14ea90 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir @@ -8,21 +8,21 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _4: std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 let mut _5: std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 let mut _6: &mut std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 - let mut _10: std::option::Option; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 - let mut _13: isize; // in scope 0 at $DIR/slice_iter.rs:+1:5: +4:6 - let mut _15: usize; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 - let mut _16: bool; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 - let mut _18: &impl Fn(usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:10 - let mut _19: (usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:16 - let _20: (); // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 + let mut _12: std::option::Option; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 + let mut _15: isize; // in scope 0 at $DIR/slice_iter.rs:+1:5: +4:6 + let mut _17: usize; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 + let mut _18: bool; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 + let mut _20: &impl Fn(usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:10 + let mut _21: (usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:16 + let _22: (); // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 scope 1 { debug iter => _5; // in scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - let _14: usize; // in scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 + let _16: usize; // in scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 scope 2 { - debug i => _14; // in scope 2 at $DIR/slice_iter.rs:+1:9: +1:10 - let _17: &T; // in scope 2 at $DIR/slice_iter.rs:+2:13: +2:14 + debug i => _16; // in scope 2 at $DIR/slice_iter.rs:+1:9: +1:10 + let _19: &T; // in scope 2 at $DIR/slice_iter.rs:+2:13: +2:14 scope 3 { - debug x => _17; // in scope 3 at $DIR/slice_iter.rs:+2:13: +2:14 + debug x => _19; // in scope 3 at $DIR/slice_iter.rs:+2:13: +2:14 } } scope 5 (inlined iter::range::>::next) { // at $DIR/slice_iter.rs:49:14: 49:28 @@ -31,14 +31,20 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug self => _6; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _8: &usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _9: bool; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _11: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _12: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _11: bool; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _13: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _14: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { - debug old => _11; // in scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _13; // in scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 8 { } } + scope 9 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _7; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _8; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _10: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -57,99 +63,99 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb1: { - StorageLive(_10); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + StorageLive(_12); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 _6 = &mut _5; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - StorageLive(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_9); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_8); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL _8 = &((*_6).1: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = ::lt(move _7, move _8) -> [return: bb2, unwind: bb13]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a usize, &'b usize) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_9); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = (*_8); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _11 = Lt(move _9, move _10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_8); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_7); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _9) -> [0: bb3, otherwise: bb4]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _11) -> [0: bb2, otherwise: bb3]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _10 = Option::::None; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _12 = Option::::None; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _11 = ((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_12); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = ::forward_unchecked(_11, const 1_usize) -> [return: bb5, unwind: bb13]; // scope 8 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _13 = ((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_14); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = ::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb12]; // scope 8 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(usize, usize) -> usize {::forward_unchecked}, val: Value() } } + bb4: { + ((*_6).0: usize) = move _14; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_14); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _12 = Option::::Some(_13); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + } + bb5: { - ((*_6).0: usize) = move _12; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_12); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _10 = Option::::Some(_11); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _15 = discriminant(_12); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb11]; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 } bb6: { - StorageDead(_9); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _13 = discriminant(_10); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - switchInt(move _13) -> [0: bb7, 1: bb9, otherwise: bb12]; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + StorageDead(_12); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 + StorageDead(_5); // scope 0 at $DIR/slice_iter.rs:+4:5: +4:6 + drop(_2) -> bb7; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 } bb7: { - StorageDead(_10); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 - StorageDead(_5); // scope 0 at $DIR/slice_iter.rs:+4:5: +4:6 - drop(_2) -> bb8; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 + return; // scope 0 at $DIR/slice_iter.rs:+5:2: +5:2 } bb8: { - return; // scope 0 at $DIR/slice_iter.rs:+5:2: +5:2 + _16 = ((_12 as Some).0: usize); // scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 + _17 = Len((*_1)); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 + _18 = Lt(_16, _17); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 + assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, _16) -> [success: bb9, unwind: bb12]; // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 } bb9: { - _14 = ((_10 as Some).0: usize); // scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 - _15 = Len((*_1)); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - _16 = Lt(_14, _15); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - assert(move _16, "index out of bounds: the length is {} but the index is {}", move _15, _14) -> [success: bb10, unwind: bb13]; // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - } - - bb10: { - _17 = &(*_1)[_14]; // scope 2 at $DIR/slice_iter.rs:+2:17: +2:26 - StorageLive(_18); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 - _18 = &_2; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 - StorageLive(_19); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 - _19 = (_14, _17); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 - _20 = >::call(move _18, move _19) -> [return: bb11, unwind: bb13]; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _19 = &(*_1)[_16]; // scope 2 at $DIR/slice_iter.rs:+2:17: +2:26 + StorageLive(_20); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 + _20 = &_2; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 + StorageLive(_21); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _21 = (_16, _19); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _22 = >::call(move _20, move _21) -> [return: bb10, unwind: bb12]; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 // mir::Constant // + span: $DIR/slice_iter.rs:51:9: 51:10 // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a impl Fn(usize, &T), (usize, &T)) -> >::Output {>::call}, val: Value() } } - bb11: { - StorageDead(_19); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 - StorageDead(_18); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 - StorageDead(_10); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 + bb10: { + StorageDead(_21); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 + StorageDead(_20); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 + StorageDead(_12); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 goto -> bb1; // scope 1 at $DIR/slice_iter.rs:+1:5: +4:6 } - bb12: { + bb11: { unreachable; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 } - bb13 (cleanup): { - drop(_2) -> [return: bb14, unwind terminate]; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 + bb12 (cleanup): { + drop(_2) -> [return: bb13, unwind terminate]; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 } - bb14 (cleanup): { + bb13 (cleanup): { resume; // scope 0 at $DIR/slice_iter.rs:+0:1: +5:2 } } From f44fc271d459d280c733f39b8af8747875eecfa9 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 3 Jun 2023 17:24:10 +0330 Subject: [PATCH 643/806] Remove unnecessary `StorageDead` --- crates/hir-ty/src/infer/closure.rs | 105 ++--- crates/hir-ty/src/mir/lower.rs | 27 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 385 +++++++++--------- crates/hir-ty/src/utils.rs | 22 +- 4 files changed, 238 insertions(+), 301 deletions(-) diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 662f2e5fa161d..754ac88bb50ce 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -9,10 +9,7 @@ use chalk_ir::{ }; use hir_def::{ data::adt::VariantData, - hir::{ - Array, BinaryOp, BindingAnnotation, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, - Statement, UnaryOp, - }, + hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, lang_item::LangItem, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, FieldId, HasModule, VariantId, @@ -28,9 +25,9 @@ use crate::{ mir::{BorrowKind, MirSpan, ProjectionElem}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - utils::{self, generics, pattern_matching_dereference_count, Generics}, - Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, ConstValue, DynTy, FnPointer, FnSig, - Interner, Substitution, Ty, TyExt, + utils::{self, generics, Generics}, + Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, ConstValue, DynTy, + FnPointer, FnSig, Interner, Substitution, Ty, TyExt, }; use super::{Expectation, InferenceContext}; @@ -488,13 +485,7 @@ impl InferenceContext<'_> { if let Some(initializer) = initializer { self.walk_expr(*initializer); if let Some(place) = self.place_of_expr(*initializer) { - let ty = self.expr_ty(*initializer); - self.consume_with_pat( - place, - ty, - BindingAnnotation::Unannotated, - *pat, - ); + self.consume_with_pat(place, *pat); } } } @@ -799,41 +790,37 @@ impl InferenceContext<'_> { } } - fn consume_with_pat( - &mut self, - mut place: HirPlace, - mut ty: Ty, - mut bm: BindingAnnotation, - pat: PatId, - ) { + fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) { + let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default(); + place.projections = place + .projections + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(); match &self.body[pat] { Pat::Missing | Pat::Wild => (), Pat::Tuple { args, ellipsis } => { - pattern_matching_dereference(&mut ty, &mut bm, &mut place); let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); - let subst = match ty.kind(Interner) { - TyKind::Tuple(_, s) => s, + let field_count = match self.result[pat].kind(Interner) { + TyKind::Tuple(_, s) => s.len(Interner), _ => return, }; - let fields = subst.iter(Interner).map(|x| x.assert_ty_ref(Interner)).enumerate(); + let fields = 0..field_count; let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); - for (arg, (i, ty)) in it { + for (arg, i) in it { let mut p = place.clone(); p.projections.push(ProjectionElem::TupleOrClosureField(i)); - self.consume_with_pat(p, ty.clone(), bm, *arg); + self.consume_with_pat(p, *arg); } } Pat::Or(pats) => { for pat in pats.iter() { - self.consume_with_pat(place.clone(), ty.clone(), bm, *pat); + self.consume_with_pat(place.clone(), *pat); } } Pat::Record { args, .. } => { - pattern_matching_dereference(&mut ty, &mut bm, &mut place); - let subst = match ty.kind(Interner) { - TyKind::Adt(_, s) => s, - _ => return, - }; let Some(variant) = self.result.variant_resolution_for_pat(pat) else { return; }; @@ -843,7 +830,6 @@ impl InferenceContext<'_> { } VariantId::StructId(s) => { let vd = &*self.db.struct_data(s).variant_data; - let field_types = self.db.field_types(variant); for field_pat in args.iter() { let arg = field_pat.pat; let Some(local_id) = vd.field(&field_pat.name) else { @@ -854,12 +840,7 @@ impl InferenceContext<'_> { parent: variant.into(), local_id, })); - self.consume_with_pat( - p, - field_types[local_id].clone().substitute(Interner, subst), - bm, - arg, - ); + self.consume_with_pat(p, arg); } } } @@ -870,26 +851,20 @@ impl InferenceContext<'_> { | Pat::Path(_) | Pat::Lit(_) => self.consume_place(place, pat.into()), Pat::Bind { id, subpat: _ } => { - let mode = self.body.bindings[*id].mode; - if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { - bm = mode; - } - let capture_kind = match bm { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { + let mode = self.result.binding_modes[*id]; + let capture_kind = match mode { + BindingMode::Move => { self.consume_place(place, pat.into()); return; } - BindingAnnotation::Ref => BorrowKind::Shared, - BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false }, + BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, + BindingMode::Ref(Mutability::Mut) => { + BorrowKind::Mut { allow_two_phase_borrow: false } + } }; self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into()); } Pat::TupleStruct { path: _, args, ellipsis } => { - pattern_matching_dereference(&mut ty, &mut bm, &mut place); - let subst = match ty.kind(Interner) { - TyKind::Adt(_, s) => s, - _ => return, - }; let Some(variant) = self.result.variant_resolution_for_pat(pat) else { return; }; @@ -903,29 +878,20 @@ impl InferenceContext<'_> { let fields = vd.fields().iter(); let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); - let field_types = self.db.field_types(variant); for (arg, (i, _)) in it { let mut p = place.clone(); p.projections.push(ProjectionElem::Field(FieldId { parent: variant.into(), local_id: i, })); - self.consume_with_pat( - p, - field_types[i].clone().substitute(Interner, subst), - bm, - *arg, - ); + self.consume_with_pat(p, *arg); } } } } Pat::Ref { pat, mutability: _ } => { - if let Some((inner, _, _)) = ty.as_reference() { - ty = inner.clone(); - place.projections.push(ProjectionElem::Deref); - self.consume_with_pat(place, ty, bm, *pat) - } + place.projections.push(ProjectionElem::Deref); + self.consume_with_pat(place, *pat) } Pat::Box { .. } => (), // not supported } @@ -1054,12 +1020,3 @@ fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option } Some(r) } - -fn pattern_matching_dereference( - cond_ty: &mut Ty, - binding_mode: &mut BindingAnnotation, - cond_place: &mut HirPlace, -) { - let cnt = pattern_matching_dereference_count(cond_ty, binding_mode); - cond_place.projections.extend((0..cnt).map(|_| ProjectionElem::Deref)); -} diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index ebd419983551c..ef94b3650bce4 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -478,9 +478,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current, None, cond_place, - self.expr_ty_after_adjustments(*expr), *pat, - BindingAnnotation::Unannotated, )?; self.write_bytes_to_place( then_target, @@ -598,16 +596,13 @@ impl<'ctx> MirLowerCtx<'ctx> { 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() { let (then, mut otherwise) = self.pattern_match( current, None, cond_place.clone(), - cond_ty.clone(), *pat, - BindingAnnotation::Unannotated, )?; let then = if let &Some(guard) = guard { let next = self.new_basic_block(); @@ -1477,9 +1472,6 @@ impl<'ctx> MirLowerCtx<'ctx> { span: MirSpan, ) -> Result<()> { self.drop_scopes.last_mut().unwrap().locals.push(l); - // FIXME: this storage dead is not neccessary, but since drop scope handling is broken, we need - // it to avoid falso positives in mutability errors - self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); Ok(()) } @@ -1508,14 +1500,8 @@ impl<'ctx> MirLowerCtx<'ctx> { 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, - )?; + (current, else_block) = + self.pattern_match(current, None, init_place, *pat)?; match (else_block, else_branch) { (None, _) => (), (Some(else_block), None) => { @@ -1595,14 +1581,7 @@ impl<'ctx> MirLowerCtx<'ctx> { continue; } } - let r = self.pattern_match( - current, - None, - local.into(), - self.result.locals[local].ty.clone(), - param, - BindingAnnotation::Unannotated, - )?; + let r = self.pattern_match(current, None, local.into(), param)?; if let Some(b) = r.1 { self.set_terminator(b, TerminatorKind::Unreachable, param.into()); } diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index ee2a0306d5ee9..5cd1be6842439 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -2,7 +2,7 @@ use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId}; -use crate::utils::pattern_matching_dereference_count; +use crate::BindingMode; use super::*; @@ -18,6 +18,26 @@ pub(super) enum AdtPatternShape<'a> { Unit, } +/// We need to do pattern matching in two phases: One to check if the pattern matches, and one to fill the bindings +/// of patterns. This is necessary to prevent double moves and similar problems. For example: +/// ```ignore +/// struct X; +/// match (X, 3) { +/// (b, 2) | (b, 3) => {}, +/// _ => {} +/// } +/// ``` +/// If we do everything in one pass, we will move `X` to the first `b`, then we see that the second field of tuple +/// doesn't match and we should move the `X` to the second `b` (which here is the same thing, but doesn't need to be) and +/// it might even doesn't match the second pattern and we may want to not move `X` at all. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum MatchingMode { + /// Check that if this pattern matches + Check, + /// Assume that this pattern matches, fill bindings + Bind, +} + impl MirLowerCtx<'_> { /// 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 @@ -29,20 +49,50 @@ impl MirLowerCtx<'_> { /// 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. pub(super) fn pattern_match( + &mut self, + current: BasicBlockId, + current_else: Option, + cond_place: Place, + pattern: PatId, + ) -> Result<(BasicBlockId, Option)> { + let (current, current_else) = self.pattern_match_inner( + current, + current_else, + cond_place.clone(), + pattern, + MatchingMode::Check, + )?; + let (current, current_else) = self.pattern_match_inner( + current, + current_else, + cond_place, + pattern, + MatchingMode::Bind, + )?; + Ok((current, current_else)) + } + + fn pattern_match_inner( &mut self, mut current: BasicBlockId, mut current_else: Option, mut cond_place: Place, - mut cond_ty: Ty, pattern: PatId, - mut binding_mode: BindingAnnotation, + mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { + let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); + cond_place.projection = cond_place + .projection + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(); Ok(match &self.body.pats[pattern] { Pat::Missing => return Err(MirLowerError::IncompletePattern), 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) { + let subst = match self.infer[pattern].kind(Interner) { TyKind::Tuple(_, s) => s, _ => { return Err(MirLowerError::TypeError( @@ -55,25 +105,31 @@ impl MirLowerCtx<'_> { current_else, args, *ellipsis, - subst.iter(Interner).enumerate().map(|(i, x)| { - (PlaceElem::TupleOrClosureField(i), x.assert_ty_ref(Interner).clone()) - }), - &cond_place, - binding_mode, + (0..subst.len(Interner)).map(|i| PlaceElem::TupleOrClosureField(i)), + &(&mut cond_place), + 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( + let (mut next, next_else) = self.pattern_match_inner( current, None, - cond_place.clone(), - cond_ty.clone(), + (&mut cond_place).clone(), *pat, - binding_mode, + MatchingMode::Check, )?; + if mode == MatchingMode::Bind { + (next, _) = self.pattern_match_inner( + next, + None, + (&mut cond_place).clone(), + *pat, + MatchingMode::Bind, + )?; + } self.set_goto(next, then_target, pattern.into()); match next_else { Some(t) => { @@ -86,8 +142,12 @@ impl MirLowerCtx<'_> { } } if !finished { - let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); - self.set_goto(current, ce, pattern.into()); + if mode == MatchingMode::Bind { + self.set_terminator(current, TerminatorKind::Unreachable, pattern.into()); + } else { + let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); + self.set_goto(current, ce, pattern.into()); + } } (then_target, current_else) } @@ -96,19 +156,19 @@ impl MirLowerCtx<'_> { not_supported!("unresolved variant for record"); }; self.pattern_matching_variant( - cond_ty, - binding_mode, cond_place, variant, current, pattern.into(), current_else, AdtPatternShape::Record { args: &*args }, + mode, )? } Pat::Range { start, end } => { let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> { - let lv = self.lower_literal_or_const_to_operand(cond_ty.clone(), l)?; + let lv = + self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); let discr: Place = @@ -116,7 +176,11 @@ impl MirLowerCtx<'_> { self.push_assignment( current, discr.clone(), - Rvalue::CheckedBinaryOp(binop, lv, Operand::Copy(cond_place.clone())), + Rvalue::CheckedBinaryOp( + binop, + lv, + Operand::Copy((&mut cond_place).clone()), + ), pattern.into(), ); let discr = Operand::Copy(discr); @@ -131,24 +195,25 @@ impl MirLowerCtx<'_> { current = next; Ok(()) }; - if let Some(start) = start { - add_check(start, BinOp::Le)?; - } - if let Some(end) = end { - add_check(end, BinOp::Ge)?; + if mode == MatchingMode::Check { + if let Some(start) = start { + add_check(start, BinOp::Le)?; + } + if let Some(end) = end { + add_check(end, BinOp::Ge)?; + } } (current, current_else) } Pat::Slice { prefix, slice, suffix } => { - pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); - if let TyKind::Slice(_) = cond_ty.kind(Interner) { + if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) { let pattern_len = prefix.len() + suffix.len(); let place_len: Place = self.temp(TyBuilder::usize(), current, pattern.into())?.into(); self.push_assignment( current, place_len.clone(), - Rvalue::Len(cond_place.clone()), + Rvalue::Len((&mut cond_place).clone()), pattern.into(), ); let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); @@ -193,63 +258,49 @@ impl MirLowerCtx<'_> { current = next; } for (i, &pat) in prefix.iter().enumerate() { - let next_place = cond_place.project(ProjectionElem::ConstantIndex { + let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { offset: i as u64, from_end: false, }); - let cond_ty = self.infer[pat].clone(); - (current, current_else) = self.pattern_match( - current, - current_else, - next_place, - cond_ty, - pat, - binding_mode, - )?; + (current, current_else) = + self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } if let Some(slice) = slice { - if let Pat::Bind { id, subpat: _ } = self.body[*slice] { - let next_place = cond_place.project(ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }); - (current, current_else) = self.pattern_match_binding( - id, - &mut binding_mode, - next_place, - (*slice).into(), - current, - current_else, - )?; + if mode == MatchingMode::Bind { + if let Pat::Bind { id, subpat: _ } = self.body[*slice] { + let next_place = (&mut cond_place).project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); + (current, current_else) = self.pattern_match_binding( + id, + next_place, + (*slice).into(), + current, + current_else, + )?; + } } } for (i, &pat) in suffix.iter().enumerate() { - let next_place = cond_place.project(ProjectionElem::ConstantIndex { + let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { offset: i as u64, from_end: true, }); - let cond_ty = self.infer[pat].clone(); - (current, current_else) = self.pattern_match( - current, - current_else, - next_place, - cond_ty, - pat, - binding_mode, - )?; + (current, current_else) = + self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } (current, current_else) } Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) { Some(variant) => self.pattern_matching_variant( - cond_ty, - binding_mode, cond_place, variant, current, pattern.into(), current_else, AdtPatternShape::Unit, + mode, )?, None => { let unresolved_name = || MirLowerError::unresolved_path(self.db, p); @@ -270,9 +321,17 @@ impl MirLowerCtx<'_> { } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = self.temp(cond_ty.clone(), current, pattern.into())?.into(); + let tmp: Place = + self.temp(self.infer[pattern].clone(), current, pattern.into())?.into(); let span = pattern.into(); - self.lower_const(c.into(), current, tmp.clone(), subst, span, cond_ty.clone())?; + self.lower_const( + c.into(), + current, + tmp.clone(), + subst, + span, + self.infer[pattern].clone(), + )?; let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); self.push_assignment( current, @@ -299,61 +358,58 @@ impl MirLowerCtx<'_> { }, Pat::Lit(l) => match &self.body.exprs[*l] { Expr::Literal(l) => { - let c = self.lower_literal_to_operand(cond_ty, l)?; - self.pattern_match_const(current_else, current, c, cond_place, pattern)? + let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?; + if mode == MatchingMode::Check { + self.pattern_match_const(current_else, current, c, cond_place, pattern)? + } else { + (current, current_else) + } } _ => not_supported!("expression path literal"), }, Pat::Bind { id, subpat } => { if let Some(subpat) = subpat { - (current, current_else) = self.pattern_match( + (current, current_else) = self.pattern_match_inner( current, current_else, - cond_place.clone(), - cond_ty, + (&mut cond_place).clone(), *subpat, - binding_mode, + mode, )? } - self.pattern_match_binding( - *id, - &mut binding_mode, - cond_place, - pattern.into(), - current, - current_else, - )? + if mode == MatchingMode::Bind { + self.pattern_match_binding( + *id, + cond_place, + pattern.into(), + current, + current_else, + )? + } else { + (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, AdtPatternShape::Tuple { args, ellipsis: *ellipsis }, + mode, )? } - Pat::Ref { pat, mutability: _ } => { - if let Some((ty, _, _)) = cond_ty.as_reference() { - cond_ty = ty.clone(); - self.pattern_match( - current, - current_else, - cond_place.project(ProjectionElem::Deref), - cond_ty, - *pat, - binding_mode, - )? - } else { - return Err(MirLowerError::TypeError("& pattern for non reference")); - } - } + Pat::Ref { pat, mutability: _ } => self.pattern_match_inner( + current, + current_else, + cond_place.project(ProjectionElem::Deref), + *pat, + mode, + )?, Pat::Box { .. } => not_supported!("box pattern"), Pat::ConstBlock(_) => not_supported!("const block pattern"), }) @@ -362,27 +418,21 @@ impl MirLowerCtx<'_> { fn pattern_match_binding( &mut self, id: BindingId, - binding_mode: &mut BindingAnnotation, cond_place: Place, span: MirSpan, current: BasicBlockId, current_else: Option, ) -> Result<(BasicBlockId, Option)> { let target_place = self.binding_local(id)?; - let mode = self.body.bindings[id].mode; - if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { - *binding_mode = mode; - } + let mode = self.infer.binding_modes[id]; 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 => { + match mode { + BindingMode::Move => Operand::Copy(cond_place).into(), + BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place), + BindingMode::Ref(Mutability::Mut) => { Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, cond_place) } }, @@ -420,52 +470,48 @@ impl MirLowerCtx<'_> { Ok((then_target, Some(else_target))) } - pub(super) fn pattern_matching_variant( + fn pattern_matching_variant( &mut self, - mut cond_ty: Ty, - mut binding_mode: BindingAnnotation, - mut cond_place: Place, + cond_place: Place, variant: VariantId, - current: BasicBlockId, + mut current: BasicBlockId, span: MirSpan, - current_else: Option, + mut current_else: Option, shape: AdtPatternShape<'_>, + mode: MatchingMode, ) -> 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")), - }; Ok(match variant { VariantId::EnumVariantId(v) => { - let e = self.const_eval_discriminant(v)? as u128; - let tmp = self.discr_temp_place(current); - self.push_assignment( - current, - tmp.clone(), - Rvalue::Discriminant(cond_place.clone()), - span, - ); - let next = self.new_basic_block(); - let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - self.set_terminator( - current, - TerminatorKind::SwitchInt { - discr: Operand::Copy(tmp), - targets: SwitchTargets::static_if(e, next, else_target), - }, - span, - ); + if mode == MatchingMode::Check { + let e = self.const_eval_discriminant(v)? as u128; + let tmp = self.discr_temp_place(current); + self.push_assignment( + current, + tmp.clone(), + Rvalue::Discriminant(cond_place.clone()), + span, + ); + let next = self.new_basic_block(); + let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(tmp), + targets: SwitchTargets::static_if(e, next, *else_target), + }, + span, + ); + current = next; + } let enum_data = self.db.enum_data(v.parent); self.pattern_matching_variant_fields( shape, &enum_data.variants[v.local_id].variant_data, variant, - subst, - next, - Some(else_target), + current, + current_else, &cond_place, - binding_mode, + mode, )? } VariantId::StructId(s) => { @@ -474,11 +520,10 @@ impl MirLowerCtx<'_> { shape, &struct_data.variant_data, variant, - subst, current, current_else, &cond_place, - binding_mode, + mode, )? } VariantId::UnionId(_) => { @@ -492,13 +537,11 @@ impl MirLowerCtx<'_> { shape: AdtPatternShape<'_>, variant_data: &VariantData, v: VariantId, - subst: &Substitution, current: BasicBlockId, current_else: Option, cond_place: &Place, - binding_mode: BindingAnnotation, + mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { - let fields_type = self.db.field_types(v); Ok(match shape { AdtPatternShape::Record { args } => { let it = args @@ -509,25 +552,16 @@ impl MirLowerCtx<'_> { Ok(( PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), x.pat, - fields_type[field_id].clone().substitute(Interner, subst), )) }) .collect::>>()?; - self.pattern_match_adt( - current, - current_else, - it.into_iter(), - cond_place, - binding_mode, - )? + self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, _)| { - ( - PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }); + let fields = variant_data + .fields() + .iter() + .map(|(x, _)| PlaceElem::Field(FieldId { parent: v.into(), local_id: x })); self.pattern_match_tuple_like( current, current_else, @@ -535,7 +569,7 @@ impl MirLowerCtx<'_> { ellipsis, fields, cond_place, - binding_mode, + mode, )? } AdtPatternShape::Unit => (current, current_else), @@ -546,14 +580,14 @@ impl MirLowerCtx<'_> { &mut self, mut current: BasicBlockId, mut current_else: Option, - args: impl Iterator, + args: impl Iterator, cond_place: &Place, - binding_mode: BindingAnnotation, + mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { - for (proj, arg, ty) in args { + for (proj, arg) in args { let cond_place = cond_place.project(proj); (current, current_else) = - self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?; + self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } Ok((current, current_else)) } @@ -564,31 +598,16 @@ impl MirLowerCtx<'_> { current_else: Option, args: &[PatId], ellipsis: Option, - fields: impl DoubleEndedIterator + Clone, + fields: impl DoubleEndedIterator + Clone, cond_place: &Place, - binding_mode: BindingAnnotation, + mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); let it = al .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())) - .map(|(x, y)| (y.0, *x, y.1)); - self.pattern_match_adt(current, current_else, it, cond_place, binding_mode) + .map(|(x, y)| (y, *x)); + self.pattern_match_adt(current, current_else, it, cond_place, mode) } } - -fn pattern_matching_dereference( - cond_ty: &mut Ty, - binding_mode: &mut BindingAnnotation, - cond_place: &mut Place, -) { - let cnt = pattern_matching_dereference_count(cond_ty, binding_mode); - cond_place.projection = cond_place - .projection - .iter() - .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::>() - .into(); -} diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 8f36188b78a3d..681d087ede6ea 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -7,7 +7,7 @@ use base_db::CrateId; use chalk_ir::{ cast::Cast, fold::{FallibleTypeFolder, Shift}, - BoundVar, DebruijnIndex, Mutability, + BoundVar, DebruijnIndex, }; use either::Either; use hir_def::{ @@ -16,7 +16,6 @@ use hir_def::{ GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, - hir::BindingAnnotation, lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, @@ -35,7 +34,7 @@ use crate::{ layout::{Layout, TagEncoding}, mir::pad16, ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, - Ty, TyExt, WhereClause, + Ty, WhereClause, }; pub(crate) fn fn_traits( @@ -395,23 +394,6 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { } } -pub(crate) fn pattern_matching_dereference_count( - cond_ty: &mut Ty, - binding_mode: &mut BindingAnnotation, -) -> usize { - let mut r = 0; - 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(); - r += 1; - } - r -} - pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> { pub(crate) db: &'a dyn HirDatabase, } From 268b08b01bfd95d315c03a9e3a69f254176b2a35 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 3 Jun 2023 17:17:56 +0200 Subject: [PATCH 644/806] do not use ty_adt_id from internal trait --- .../src/traits/error_reporting/suggestions.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 9f2740a3898db..1044613c585c3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -27,7 +27,6 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; use rustc_middle::hir::map; -use rustc_middle::query::Key; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, @@ -3701,8 +3700,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let Some(typeck_results) = self.typeck_results.as_ref() else { return }; // Make sure we're dealing with the `Option` type. - let Some(ty_adt_did) = typeck_results.expr_ty_adjusted(expr).ty_adt_id() else { return }; - if !tcx.is_diagnostic_item(sym::Option, ty_adt_did) { + let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return }; + if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) { return; } From 5c77a0d7a7543addab9ca5c4c32c330fb8620e83 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 3 Jun 2023 17:08:29 +0200 Subject: [PATCH 645/806] Remove unneeded `Buffer` allocations when `&mut fmt::Write` can be used directly --- src/librustdoc/html/render/mod.rs | 4 +-- src/librustdoc/html/render/print_item.rs | 37 ++++++++++-------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a5223bd6309d7..df17ab07b576d 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1038,9 +1038,9 @@ fn render_attributes_in_pre<'a, 'b: 'a>( // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. -fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item, tcx: TyCtxt<'_>) { +fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) { for a in it.attributes(tcx, false) { - write!(w, "

", a); + write!(w, "
{}
", a).unwrap(); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 1d66805bd5c06..5bf5b9ef61809 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1431,30 +1431,28 @@ fn item_proc_macro( it: &clean::Item, m: &clean::ProcMacro, ) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { + wrap_item(w, |buffer| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(buffer, "{}!() {{ /* proc-macro */ }}", name); + write!(buffer, "{}!() {{ /* proc-macro */ }}", name).unwrap(); } MacroKind::Attr => { - write!(buffer, "#[{}]", name); + write!(buffer, "#[{}]", name).unwrap(); } MacroKind::Derive => { - write!(buffer, "#[derive({})]", name); + write!(buffer, "#[derive({})]", name).unwrap(); if !m.helpers.is_empty() { - buffer.push_str("\n{\n"); - buffer.push_str(" // Attributes available to this derive:\n"); + buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap(); for attr in &m.helpers { - writeln!(buffer, " #[{}]", attr); + writeln!(buffer, " #[{}]", attr).unwrap(); } - buffer.push_str("}\n"); + buffer.write_str("}\n").unwrap(); } } } }); - write!(w, "{}{}", buffer.into_inner(), document(cx, it, None, HeadingOffset::H2)).unwrap(); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1571,8 +1569,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { + wrap_item(w, |buffer| { render_attributes_in_code(buffer, it, cx.tcx()); write!( buffer, @@ -1581,29 +1578,27 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, mutability = s.mutability.print_with_space(), name = it.name.unwrap(), typ = s.type_.print(cx) - ); + ) + .unwrap(); }); - write!(w, "{}", buffer.into_inner()).unwrap(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { - buffer.write_str("extern {\n"); + wrap_item(w, |buffer| { + buffer.write_str("extern {\n").unwrap(); render_attributes_in_code(buffer, it, cx.tcx()); write!( buffer, " {}type {};\n}}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), it.name.unwrap(), - ); + ) + .unwrap(); }); - write!(w, "{}{}", buffer.into_inner(), document(cx, it, None, HeadingOffset::H2)).unwrap(); - + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) .unwrap(); } From ca720fdfee83c7b63ee5acb29b6b5b07a493e236 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 3 Jun 2023 16:52:33 +0200 Subject: [PATCH 646/806] miri compiletest: no longer allow some warnings in rustc test suite --- src/tools/miri/tests/compiletest.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 71959fa115892..9e6e37031535e 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -51,18 +51,9 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> let mut program = CommandBuilder::rustc(); program.program = miri_path(); - let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some(); - // Add some flags we always want. - if in_rustc_test_suite { - // Less aggressive warnings to make the rustc toolstate management less painful. - // (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.) - program.args.push("-Astable-features".into()); - program.args.push("-Aunused".into()); - } else { - program.args.push("-Dwarnings".into()); - program.args.push("-Dunused".into()); - } + program.args.push("-Dwarnings".into()); + program.args.push("-Dunused".into()); if let Ok(extra_flags) = env::var("MIRIFLAGS") { for flag in extra_flags.split_whitespace() { program.args.push(flag.into()); From 17d321cd11a938ac3eaeeded12969d6b83f9aa26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sat, 3 Jun 2023 16:19:09 +0000 Subject: [PATCH 647/806] rust-lld: add rpath to the root LLVM shared lib rust-lld is not located in the same directory as the other binaries that point to ../lib, but in a deeper directory in lib. So we need to point a few layers up for rust-lld to find the LLVM shared library without rustup's LD_LIBRARY_PATH overrides. --- src/bootstrap/llvm.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 3fd0cca40e522..ab7906d038302 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -834,6 +834,30 @@ impl Step for Lld { } } + // LLD is built as an LLVM tool, but is distributed outside of the `llvm-tools` component, + // which impacts where it expects to find LLVM's shared library. This causes #80703. + // + // LLD is distributed at "$root/lib/rustlib/$host/bin/rust-lld", but the `libLLVM-*.so` it + // needs is distributed at "$root/lib". The default rpath of "$ORIGIN/../lib" points at the + // lib path for LLVM tools, not the one for rust binaries. + // + // (The `llvm-tools` component copies the .so there for the other tools, and with that + // component installed, one can successfully invoke `rust-lld` directly without rustup's + // `LD_LIBRARY_PATH` overrides) + // + if builder.config.rust_rpath + && builder.config.llvm_link_shared() + && target.contains("linux") + { + // So we inform LLD where it can find LLVM's libraries by adding an rpath entry to the + // expected parent `lib` directory. + // + // Be careful when changing this path, we need to ensure it's quoted or escaped: + // `$ORIGIN` would otherwise be expanded when the `LdFlags` are passed verbatim to + // cmake. + ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); + } + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); From 3591a1239c123588b03c7600b54cc573187980a3 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 31 May 2023 11:36:58 -0500 Subject: [PATCH 648/806] Allow disabling truncation for long config lines --- src/bootstrap/configure.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 571062a3a6fd0..4481e1668b60f 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -45,7 +45,6 @@ def v(*args): o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)") o("rpath", "rust.rpath", "build rpaths into rustc itself") o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests") -o("option-checking", None, "complain about unrecognized options in this configure script") o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("vendor", "build.vendor", "enable usage of vendored Rust crates") @@ -170,6 +169,9 @@ def v(*args): v("host", None, "List of GNUs ./configure syntax LLVM host triples") v("target", None, "List of GNUs ./configure syntax LLVM target triples") +# Options specific to this configure script +o("option-checking", None, "complain about unrecognized options in this configure script") +o("verbose-configure", None, "don't truncate options when printing them in this configure script") v("set", None, "set arbitrary key/value pairs in TOML configuration") @@ -211,6 +213,8 @@ def is_value_list(key): print('be passed with `--disable-foo` to forcibly disable the option') sys.exit(0) +VERBOSE = False + # Parse all command line arguments into one of these three lists, handling # boolean and value-based options separately def parse_args(args): @@ -271,6 +275,9 @@ def parse_args(args): if len(need_value_args) > 0: err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0])) + global VERBOSE + VERBOSE = 'verbose-configure' in known_args + config = {} set('build.configure-args', sys.argv[1:], config) @@ -290,7 +297,7 @@ def set(key, value, config): value = [v for v in value if v] s = "{:20} := {}".format(key, value) - if len(s) < 70: + if len(s) < 70 or VERBOSE: p(s) else: p(s[:70] + " ...") @@ -371,7 +378,7 @@ def apply_args(known_args, option_checking, config): set('rust.lld', True, config) set('rust.llvm-tools', True, config) set('build.extended', True, config) - elif option.name == 'option-checking': + elif option.name in ['option-checking', 'verbose-configure']: # this was handled above pass elif option.name == 'dist-compression-formats': From 42a9898fd1a6a5302b3b5b0661c8d08532594087 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 31 May 2023 11:37:50 -0500 Subject: [PATCH 649/806] disable truncation in CI --- src/ci/run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ci/run.sh b/src/ci/run.sh index 966af3abc6f1c..5363086f68b65 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -53,6 +53,7 @@ if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch HAS_METRICS=1 fi +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-verbose-configure" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" From d0298009924a3d37ddba89b346d1e419c9f1def3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 1 Jun 2023 20:10:39 +0200 Subject: [PATCH 650/806] Update reexport-attr-merge rustdoc test --- tests/rustdoc/reexport-attr-merge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc/reexport-attr-merge.rs b/tests/rustdoc/reexport-attr-merge.rs index f6c23a1365f46..6cc054e7a8b2c 100644 --- a/tests/rustdoc/reexport-attr-merge.rs +++ b/tests/rustdoc/reexport-attr-merge.rs @@ -19,9 +19,9 @@ pub use Foo1 as Foo2; // First we ensure that only the reexport `Bar2` and the inlined struct `Bar` // are inlined. // @count - '//a[@class="struct"]' 2 -// Then we check that both `cfg` are displayed. +// Then we check that `cfg` is displayed for base item, but not for intermediate re-exports. // @has - '//*[@class="stab portability"]' 'foo' -// @has - '//*[@class="stab portability"]' 'bar' +// @!has - '//*[@class="stab portability"]' 'bar' // And finally we check that the only element displayed is `Bar`. // @has - '//a[@class="struct"]' 'Bar' #[doc(inline)] From f6611d34f13be7b105a6eda384aa25e30f3c733b Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 3 Jun 2023 14:14:54 -0500 Subject: [PATCH 651/806] Don't compile rustc to self-test compiletest This was changed from stage 0 to 1 in https://github.com/rust-lang/rust/pull/108905, but I'm not sure why. Change it to `top_stage` instead to allow people to choose the stage. This should save quite a bit of time in the `mingw-check` builder, which explicitly runs `x test --stage 0 compiletest`. --- src/bootstrap/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index eec8c4ad69f23..6a1e4556de85d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -700,7 +700,7 @@ impl Step for CompiletestTest { /// Runs `cargo test` for compiletest. fn run(self, builder: &Builder<'_>) { let host = self.host; - let compiler = builder.compiler(1, host); + let compiler = builder.compiler(builder.top_stage, host); // We need `ToolStd` for the locally-built sysroot because // compiletest uses unstable features of the `test` crate. From c57eb1bb6e8284b141f715d83c4572559652586e Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 3 Jun 2023 14:31:36 -0500 Subject: [PATCH 652/806] rename src/dst to original/link this is consistent with std and makes it much easier to understand what's actually happening --- src/bootstrap/doc.rs | 10 +++++----- src/bootstrap/util.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index d6210ed59c4d1..15337a4f8d672 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -959,21 +959,21 @@ impl Step for UnstableBookGen { } } -fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> { +fn symlink_dir_force(config: &Config, original: &Path, link: &Path) -> io::Result<()> { if config.dry_run() { return Ok(()); } - if let Ok(m) = fs::symlink_metadata(dst) { + if let Ok(m) = fs::symlink_metadata(link) { if m.file_type().is_dir() { - fs::remove_dir_all(dst)?; + fs::remove_dir_all(link)?; } else { // handle directory junctions on windows by falling back to // `remove_dir`. - fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?; + fs::remove_file(link).or_else(|_| fs::remove_dir(link))?; } } - symlink_dir(config, src, dst) + symlink_dir(config, original, link) } #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index e4bbccdb067c2..7e29f671f028b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -134,17 +134,17 @@ pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. -pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> { +pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { if config.dry_run() { return Ok(()); } - let _ = fs::remove_dir(dest); - return symlink_dir_inner(src, dest); + let _ = fs::remove_dir(link); + return symlink_dir_inner(original, link); #[cfg(not(windows))] - fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { + fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { use std::os::unix::fs; - fs::symlink(src, dest) + fs::symlink(original, link) } #[cfg(windows)] From 3f05b1fb2c90c88bbd5853928653247ad79b7047 Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 3 Jun 2023 14:45:43 -0500 Subject: [PATCH 653/806] don't return a `Result` from symlink_dir_force this gives a more helpful backtrace if it fails before: ``` thread 'main' panicked at 'symlink_dir_force(&builder.config, &out, &out_dir) failed with No such file or directory (os error 2)', doc.rs:697:9 ``` after: ``` thread 'main' panicked at 'symlink_dir(config, original, link) failed with No such file or directory (os error 2) ("failed to create link from /home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/doc -> /home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/compiler-doc")', doc.rs:975:5 ``` --- src/bootstrap/doc.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 15337a4f8d672..be3d7aacafdd8 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -8,7 +8,6 @@ //! `rustdoc`. use std::fs; -use std::io; use std::path::{Path, PathBuf}; use crate::builder::crate_description; @@ -694,11 +693,11 @@ impl Step for Rustc { // rustc. rustdoc needs to be able to see everything, for example when // merging the search index, or generating local (relative) links. let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc"); - t!(symlink_dir_force(&builder.config, &out, &out_dir)); + symlink_dir_force(&builder.config, &out, &out_dir); // Cargo puts proc macros in `target/doc` even if you pass `--target` // explicitly (https://github.com/rust-lang/cargo/issues/7677). let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc"); - t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir)); + symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); @@ -816,7 +815,7 @@ macro_rules! tool_doc { ]; for out_dir in out_dirs { t!(fs::create_dir_all(&out_dir)); - t!(symlink_dir_force(&builder.config, &out, &out_dir)); + symlink_dir_force(&builder.config, &out, &out_dir); } // Build cargo command. @@ -959,21 +958,24 @@ impl Step for UnstableBookGen { } } -fn symlink_dir_force(config: &Config, original: &Path, link: &Path) -> io::Result<()> { +fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { if config.dry_run() { - return Ok(()); + return; } if let Ok(m) = fs::symlink_metadata(link) { if m.file_type().is_dir() { - fs::remove_dir_all(link)?; + t!(fs::remove_dir_all(link)); } else { // handle directory junctions on windows by falling back to // `remove_dir`. - fs::remove_file(link).or_else(|_| fs::remove_dir(link))?; + t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link))); } } - symlink_dir(config, original, link) + t!( + symlink_dir(config, original, link), + format!("failed to create link from {} -> {}", link.display(), original.display()) + ); } #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] From d613134623d1659ece542a02b585b70cbf36c2b5 Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 3 Jun 2023 14:46:22 -0500 Subject: [PATCH 654/806] fix `x doc --stage 0 compiler` if the compiler hasn't yet been built --- src/bootstrap/doc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index be3d7aacafdd8..f3716c81e11ec 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -693,6 +693,7 @@ impl Step for Rustc { // rustc. rustdoc needs to be able to see everything, for example when // merging the search index, or generating local (relative) links. let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc"); + t!(fs::create_dir_all(out_dir.parent().unwrap())); symlink_dir_force(&builder.config, &out, &out_dir); // Cargo puts proc macros in `target/doc` even if you pass `--target` // explicitly (https://github.com/rust-lang/cargo/issues/7677). From 4117b5e65bdcace7df94fe28a621f252d4a9412d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sat, 3 Jun 2023 19:50:45 +0000 Subject: [PATCH 655/806] improve target selection conditions --- src/bootstrap/llvm.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index ab7906d038302..19e595650cf9d 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -845,7 +845,8 @@ impl Step for Lld { // component installed, one can successfully invoke `rust-lld` directly without rustup's // `LD_LIBRARY_PATH` overrides) // - if builder.config.rust_rpath + if builder.config.rpath_enabled(target) + && util::use_host_linker(target) && builder.config.llvm_link_shared() && target.contains("linux") { From 08f89193b53b3c6fe03d0ccd321d18bd518ccf24 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 4 Jun 2023 01:03:32 +0330 Subject: [PATCH 656/806] Fix drop scopes in mir --- crates/hir-ty/src/mir/eval.rs | 9 +- crates/hir-ty/src/mir/eval/tests.rs | 69 ++++++++++- crates/hir-ty/src/mir/lower.rs | 114 ++++++++++++++---- .../src/handlers/mutability_errors.rs | 56 ++++++++- crates/test-utils/src/minicore.rs | 13 ++ 5 files changed, 237 insertions(+), 24 deletions(-) diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 6778808d529db..ce14f6dbad536 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1896,7 +1896,14 @@ impl Evaluator<'_> { let mir_body = self .db .monomorphized_mir_body(def, generic_args, self.trait_env.clone()) - .map_err(|e| MirEvalError::MirLowerError(imp, e))?; + .map_err(|e| { + MirEvalError::InFunction( + Either::Left(imp), + Box::new(MirEvalError::MirLowerError(imp, e)), + span, + locals.body.owner, + ) + })?; let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) })?; diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 8c097539eb5e6..dabc76ba15bf4 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -1,5 +1,6 @@ use base_db::{fixture::WithFixture, FileId}; use hir_def::db::DefDatabase; +use syntax::{TextRange, TextSize}; use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; @@ -45,7 +46,21 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr match x { Err(e) => { let mut err = String::new(); - let span_formatter = |file, range| format!("{:?} {:?}", file, range); + let line_index = |size: TextSize| { + let mut size = u32::from(size) as usize; + let mut lines = ra_fixture.lines().enumerate(); + while let Some((i, l)) = lines.next() { + if let Some(x) = size.checked_sub(l.len()) { + size = x; + } else { + return (i, size); + } + } + (usize::MAX, size) + }; + let span_formatter = |file, range: TextRange| { + format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end())) + }; e.pretty_print(&mut err, &db, span_formatter).unwrap(); panic!("Error in interpreting: {err}"); } @@ -115,6 +130,58 @@ fn main() { ); } +#[test] +fn drop_if_let() { + check_pass( + r#" +//- minicore: drop, add, option, cell, builtin_impls + +use core::cell::Cell; + +struct X<'a>(&'a Cell); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1) + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +#[test] +fn main() { + let s = Cell::new(0); + let x = Some(X(&s)); + if let Some(y) = x { + if s.get() != 0 { + should_not_reach(); + } + if s.get() != 0 { + should_not_reach(); + } + } else { + should_not_reach(); + } + if s.get() != 1 { + should_not_reach(); + } + let x = Some(X(&s)); + if let None = x { + should_not_reach(); + } else { + if s.get() != 1 { + should_not_reach(); + } + } + if s.get() != 1 { + should_not_reach(); + } +} + "#, + ); +} + #[test] fn drop_in_place() { check_pass( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index ef94b3650bce4..aad1a82f2981c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -48,6 +48,7 @@ struct LoopBlocks { /// `None` for loops that are not terminating end: Option, place: Place, + drop_scope_index: usize, } #[derive(Debug, Clone, Default)] @@ -101,6 +102,35 @@ pub enum MirLowerError { GenericArgNotProvided(TypeOrConstParamId, Substitution), } +/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. +struct DropScopeToken; +impl DropScopeToken { + fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { + std::mem::forget(self); + ctx.pop_drop_scope_internal(current) + } + + /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop + /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled + /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't + /// do anything) + fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) { + std::mem::forget(self); + ctx.pop_drop_scope_assume_dropped_internal(); + } +} + +// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since +// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be +// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful +// stack trace. +// +// impl Drop for DropScopeToken { +// fn drop(&mut self) { +// never!("Drop scope doesn't popped"); +// } +// } + impl MirLowerError { pub fn pretty_print( &self, @@ -506,7 +536,6 @@ impl<'ctx> MirLowerCtx<'ctx> { self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { let end = this.current_loop_end()?; - let current = this.pop_drop_scope(current); this.set_goto(current, end, expr_id.into()); } Ok(()) @@ -516,30 +545,39 @@ impl<'ctx> MirLowerCtx<'ctx> { } } Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { - if let Some((_, current)) = this.lower_expr_as_place(begin, *body, true)? { - let current = this.pop_drop_scope(current); + let scope = this.push_drop_scope(); + if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { + current = scope.pop_and_drop(this, current); this.set_goto(current, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); } Ok(()) }), Expr::While { condition, body, label } => { self.lower_loop(current, place, *label, expr_id.into(),|this, begin| { + let scope = this.push_drop_scope(); let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { return Ok(()); }; - let end = this.current_loop_end()?; + let fail_cond = this.new_basic_block(); let after_cond = this.new_basic_block(); this.set_terminator( to_switch, TerminatorKind::SwitchInt { discr, - targets: SwitchTargets::static_if(1, after_cond, end), + targets: SwitchTargets::static_if(1, after_cond, fail_cond), }, expr_id.into(), ); + let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond); + let end = this.current_loop_end()?; + this.set_goto(fail_cond, end, expr_id.into()); if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { - let block = this.pop_drop_scope(block); + let block = scope.pop_and_drop(this, block); this.set_goto(block, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); } Ok(()) }) @@ -637,7 +675,9 @@ impl<'ctx> MirLowerCtx<'ctx> { Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?, }; - self.set_goto(current, loop_data.begin, expr_id.into()); + let begin = loop_data.begin; + current = self.drop_until_scope(loop_data.drop_scope_index, current); + self.set_goto(current, begin, expr_id.into()); Ok(None) }, &Expr::Break { expr, label } => { @@ -651,10 +691,16 @@ impl<'ctx> MirLowerCtx<'ctx> { }; current = c; } - let end = match label { - Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"), - None => self.current_loop_end()?, + let (end, drop_scope) = match label { + Some(l) => { + let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?; + (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index) + }, + None => { + (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index) + }, }; + current = self.drop_until_scope(drop_scope, current); self.set_goto(current, end, expr_id.into()); Ok(None) } @@ -1378,7 +1424,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let begin = self.new_basic_block(); let prev = mem::replace( &mut self.current_loop_blocks, - Some(LoopBlocks { begin, end: None, place }), + Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }), ); let prev_label = if let Some(label) = label { // We should generate the end now, to make sure that it wouldn't change later. It is @@ -1391,7 +1437,6 @@ impl<'ctx> MirLowerCtx<'ctx> { None }; self.set_goto(prev_block, begin, span); - self.push_drop_scope(); f(self, begin)?; let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or( MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()), @@ -1489,6 +1534,7 @@ impl<'ctx> MirLowerCtx<'ctx> { place: Place, span: MirSpan, ) -> Result>> { + let scope = self.push_drop_scope(); for statement in statements.iter() { match statement { hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { @@ -1497,6 +1543,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((init_place, c)) = self.lower_expr_as_place(current, *expr_id, true)? else { + scope.pop_assume_dropped(self); return Ok(None); }; current = c; @@ -1528,18 +1575,25 @@ impl<'ctx> MirLowerCtx<'ctx> { } } hir_def::hir::Statement::Expr { expr, has_semi: _ } => { - self.push_drop_scope(); + let scope2 = self.push_drop_scope(); let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + scope2.pop_assume_dropped(self); + scope.pop_assume_dropped(self); return Ok(None); }; - current = self.pop_drop_scope(c); + current = scope2.pop_and_drop(self, c); } } } - match tail { - Some(tail) => self.lower_expr_to_place(tail, place, current), - None => Ok(Some(current)), + if let Some(tail) = tail { + let Some(c) = self.lower_expr_to_place(tail, place, current)? else { + scope.pop_assume_dropped(self); + return Ok(None); + }; + current = c; } + current = scope.pop_and_drop(self, current); + Ok(Some(current)) } fn lower_params_and_bindings( @@ -1625,16 +1679,34 @@ impl<'ctx> MirLowerCtx<'ctx> { current } - fn push_drop_scope(&mut self) { + fn push_drop_scope(&mut self) -> DropScopeToken { self.drop_scopes.push(DropScope::default()); + DropScopeToken + } + + /// Don't call directly + fn pop_drop_scope_assume_dropped_internal(&mut self) { + self.drop_scopes.pop(); } - fn pop_drop_scope(&mut self, mut current: BasicBlockId) -> BasicBlockId { + /// Don't call directly + fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { let scope = self.drop_scopes.pop().unwrap(); self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); current } + fn pop_drop_scope_assert_finished( + &mut self, + mut current: BasicBlockId, + ) -> Result { + current = self.pop_drop_scope_internal(current); + if !self.drop_scopes.is_empty() { + implementation_error!("Mismatched count between drop scope push and pops"); + } + Ok(current) + } + fn emit_drop_and_storage_dead_for_scope( &mut self, scope: &DropScope, @@ -1728,7 +1800,7 @@ pub fn mir_body_for_closure_query( |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { - let current = ctx.pop_drop_scope(current); + let current = ctx.pop_drop_scope_assert_finished(current)?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } let mut upvar_map: FxHashMap> = FxHashMap::default(); @@ -1863,7 +1935,7 @@ pub fn lower_to_mir( ctx.lower_params_and_bindings([].into_iter(), binding_picker)? }; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { - let current = ctx.pop_drop_scope(current); + let current = ctx.pop_drop_scope_assert_finished(current)?; ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); } Ok(ctx.result) diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 28dadae8d3ef9..743ef0c6f5290 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -512,6 +512,38 @@ fn main() { f(x); } } +"#, + ); + check_diagnostics( + r#" +fn check(_: i32) -> bool { + false +} +fn main() { + loop { + let x = 1; + if check(x) { + break; + } + let y = (1, 2); + if check(y.1) { + return; + } + let z = (1, 2); + match z { + (k @ 5, ref mut t) if { continue; } => { + //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z` + *t = 5; + } + _ => { + let y = (1, 2); + if check(y.1) { + return; + } + } + } + } +} "#, ); check_diagnostics( @@ -592,7 +624,7 @@ fn f((x, y): (i32, i32)) { fn for_loop() { check_diagnostics( r#" -//- minicore: iterators +//- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { for (a, mut b) in x { //^^^^^ 💡 weak: variable does not need to be mutable @@ -604,6 +636,28 @@ fn f(x: [(i32, u8); 10]) { ); } + #[test] + fn while_let() { + check_diagnostics( + r#" +//- minicore: iterators, copy +fn f(x: [(i32, u8); 10]) { + let mut it = x.into_iter(); + while let Some((a, mut b)) = it.next() { + //^^^^^ 💡 weak: variable does not need to be mutable + while let Some((c, mut d)) = it.next() { + //^^^^^ 💡 weak: variable does not need to be mutable + a = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + c = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `c` + } + } +} +"#, + ); + } + #[test] fn index() { check_diagnostics( diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index c9e85e36870f9..3ca31cce5649e 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -726,6 +726,19 @@ pub mod ops { pub trait AddAssign { fn add_assign(&mut self, rhs: Rhs); } + + // region:builtin_impls + macro_rules! add_impl { + ($($t:ty)*) => ($( + impl const Add for $t { + type Output = $t; + fn add(self, other: $t) -> $t { self + other } + } + )*) + } + + add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } + // endregion:builtin_impls // endregion:add // region:generator From 5839b028d57c788e78018bc937645ed7f97d7436 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 3 Jun 2023 22:48:30 +0100 Subject: [PATCH 657/806] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index f7b95e31642e0..b0fa79679e717 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit f7b95e31642e09c2b6eabb18ed75007dda6677a0 +Subproject commit b0fa79679e717cd077b7fc0fa4166f47107f1ba9 From 2d0510e226ea857bcd2f14845740b0b20d3048a2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 4 Jun 2023 09:09:25 +0200 Subject: [PATCH 658/806] Add mandatory panic contexts to all threadpool tasks --- crates/rust-analyzer/src/dispatch.rs | 14 +- .../src/handlers/notification.rs | 14 +- crates/rust-analyzer/src/main_loop.rs | 140 ++++++++++-------- crates/rust-analyzer/src/reload.rs | 116 ++++++++------- crates/rust-analyzer/src/task_pool.rs | 28 +++- 5 files changed, 174 insertions(+), 138 deletions(-) diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index ebe77b8dfe721..3527d92a4ec4e 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -104,13 +104,10 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, { + self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, panic_context, { let world = self.global_state.snapshot(); move || { - let result = panic::catch_unwind(move || { - let _pctx = stdx::panic_context::enter(panic_context); - f(world, params) - }); + let result = panic::catch_unwind(move || f(world, params)); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), Err(_) => Task::Response(lsp_server::Response::new_err( @@ -178,13 +175,10 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(intent, { + self.global_state.task_pool.handle.spawn(intent, panic_context, { let world = self.global_state.snapshot(); move || { - let result = panic::catch_unwind(move || { - let _pctx = stdx::panic_context::enter(panic_context); - f(world, params) - }); + let result = panic::catch_unwind(move || f(world, params)); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), Err(_) => Task::Retry(req), diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 09de6900c8fb5..b3623669cc6b3 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -291,11 +291,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } Ok(()) }; - state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { - if let Err(e) = std::panic::catch_unwind(task) { - tracing::error!("flycheck task panicked: {e:?}") - } - }); + state.task_pool.handle.spawn_with_sender( + stdx::thread::ThreadIntent::Worker, + "flycheck", + move |_| { + if let Err(e) = std::panic::catch_unwind(task) { + tracing::error!("flycheck task panicked: {e:?}") + } + }, + ); true } else { false diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 19c49a23000ea..92d44eeee8941 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -397,19 +397,25 @@ impl GlobalState { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); - self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, { - let analysis = self.snapshot().analysis; - move |sender| { - sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); - let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { - let report = PrimeCachesProgress::Report(progress); - sender.send(Task::PrimeCaches(report)).unwrap(); - }); - sender - .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() })) - .unwrap(); - } - }); + self.task_pool.handle.spawn_with_sender( + stdx::thread::ThreadIntent::Worker, + "prime_caches", + { + let analysis = self.snapshot().analysis; + move |sender| { + sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); + let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { + let report = PrimeCachesProgress::Report(progress); + sender.send(Task::PrimeCaches(report)).unwrap(); + }); + sender + .send(Task::PrimeCaches(PrimeCachesProgress::End { + cancelled: res.is_err(), + })) + .unwrap(); + } + }, + ); } fn update_status_or_notify(&mut self) { @@ -796,56 +802,62 @@ impl GlobalState { // Diagnostics are triggered by the user typing // so we run them on a latency sensitive thread. - self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || { - let _p = profile::span("publish_diagnostics"); - let diagnostics = subscriptions - .into_iter() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - Some(( - file_id, - line_index, - snapshot - .analysis - .diagnostics( - &snapshot.config.diagnostics(), - ide::AssistResolveStrategy::None, - file_id, - ) - .ok()?, - )) - }) - .map(|(file_id, line_index, it)| { - ( - file_id, - it.into_iter() - .map(move |d| lsp_types::Diagnostic { - range: crate::to_proto::range(&line_index, d.range), - severity: Some(crate::to_proto::diagnostic_severity(d.severity)), - code: Some(lsp_types::NumberOrString::String( - d.code.as_str().to_string(), - )), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { - Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) - } else { - None - }, - data: None, - }) - .collect::>(), - ) - }); - Task::Diagnostics(diagnostics.collect()) - }); + self.task_pool.handle.spawn( + stdx::thread::ThreadIntent::LatencySensitive, + "publish_diagnostics", + move || { + let _p = profile::span("publish_diagnostics"); + let diagnostics = subscriptions + .into_iter() + .filter_map(|file_id| { + let line_index = snapshot.file_line_index(file_id).ok()?; + Some(( + file_id, + line_index, + snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()?, + )) + }) + .map(|(file_id, line_index, it)| { + ( + file_id, + it.into_iter() + .map(move |d| lsp_types::Diagnostic { + range: crate::to_proto::range(&line_index, d.range), + severity: Some(crate::to_proto::diagnostic_severity( + d.severity, + )), + code: Some(lsp_types::NumberOrString::String( + d.code.as_str().to_string(), + )), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&format!( + "https://rust-analyzer.github.io/manual.html#{}", + d.code.as_str() + )) + .unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: if d.unused { + Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) + } else { + None + }, + data: None, + }) + .collect::>(), + ) + }); + Task::Diagnostics(diagnostics.collect()) + }, + ); } } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 6e8c8ea91a1b3..5911e24d999c2 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -185,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, "fetch_workspaces", { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -260,19 +260,25 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { - sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); + self.task_pool.handle.spawn_with_sender( + ThreadIntent::Worker, + "fetch_build_data", + move |sender| { + sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); - let progress = { - let sender = sender.clone(); - move |msg| { - sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() - } - }; - let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); + let progress = { + let sender = sender.clone(); + move |msg| { + sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() + } + }; + let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); - sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap(); - }); + sender + .send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))) + .unwrap(); + }, + ); } pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { @@ -280,50 +286,54 @@ impl GlobalState { let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); - self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { - sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); + self.task_pool.handle.spawn_with_sender( + ThreadIntent::Worker, + "fetch_proc_macros", + move |sender| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); + + let dummy_replacements = &dummy_replacements; + let progress = { + let sender = sender.clone(); + &move |msg| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() + } + }; - let dummy_replacements = &dummy_replacements; - let progress = { - let sender = sender.clone(); - &move |msg| { - sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() + let mut res = FxHashMap::default(); + let chain = proc_macro_clients + .iter() + .map(|res| res.as_ref().map_err(|e| e.to_string())) + .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into()))); + for (client, paths) in chain.zip(paths) { + res.extend(paths.into_iter().map(move |(crate_id, res)| { + ( + crate_id, + res.map_or_else( + |_| Err("proc macro crate is missing dylib".to_owned()), + |(crate_name, path)| { + progress(path.display().to_string()); + client.as_ref().map_err(Clone::clone).and_then(|client| { + load_proc_macro( + client, + &path, + crate_name + .as_deref() + .and_then(|crate_name| { + dummy_replacements.get(crate_name).map(|v| &**v) + }) + .unwrap_or_default(), + ) + }) + }, + ), + ) + })); } - }; - - let mut res = FxHashMap::default(); - let chain = proc_macro_clients - .iter() - .map(|res| res.as_ref().map_err(|e| e.to_string())) - .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into()))); - for (client, paths) in chain.zip(paths) { - res.extend(paths.into_iter().map(move |(crate_id, res)| { - ( - crate_id, - res.map_or_else( - |_| Err("proc macro crate is missing dylib".to_owned()), - |(crate_name, path)| { - progress(path.display().to_string()); - client.as_ref().map_err(Clone::clone).and_then(|client| { - load_proc_macro( - client, - &path, - crate_name - .as_deref() - .and_then(|crate_name| { - dummy_replacements.get(crate_name).map(|v| &**v) - }) - .unwrap_or_default(), - ) - }) - }, - ), - ) - })); - } - sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); - }); + sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); + }, + ); } pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) { diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index a5a10e86914e0..823210980f2c5 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -14,25 +14,41 @@ impl TaskPool { TaskPool { sender, pool: Pool::new(threads) } } - pub(crate) fn spawn(&mut self, intent: ThreadIntent, task: F) - where + pub(crate) fn spawn( + &mut self, + intent: ThreadIntent, + panic_context: impl Into, + task: F, + ) where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { + let panic_context = panic_context.into(); self.pool.spawn(intent, { let sender = self.sender.clone(); - move || sender.send(task()).unwrap() + move || { + let _pctx = stdx::panic_context::enter(panic_context); + sender.send(task()).unwrap() + } }) } - pub(crate) fn spawn_with_sender(&mut self, intent: ThreadIntent, task: F) - where + pub(crate) fn spawn_with_sender( + &mut self, + intent: ThreadIntent, + panic_context: impl Into, + task: F, + ) where F: FnOnce(Sender) + Send + 'static, T: Send + 'static, { + let panic_context = panic_context.into(); self.pool.spawn(intent, { let sender = self.sender.clone(); - move || task(sender) + move || { + let _pctx = stdx::panic_context::enter(panic_context); + task(sender) + } }) } From a1af9eb1f840f9425dce19d661c7bb34c61e1854 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 4 Jun 2023 09:30:21 +0200 Subject: [PATCH 659/806] Revert "Add mandatory panic contexts to all threadpool tasks" --- crates/rust-analyzer/src/dispatch.rs | 14 +- .../src/handlers/notification.rs | 14 +- crates/rust-analyzer/src/main_loop.rs | 140 ++++++++---------- crates/rust-analyzer/src/reload.rs | 116 +++++++-------- crates/rust-analyzer/src/task_pool.rs | 28 +--- 5 files changed, 138 insertions(+), 174 deletions(-) diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 3527d92a4ec4e..ebe77b8dfe721 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -104,10 +104,13 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, panic_context, { + self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, { let world = self.global_state.snapshot(); move || { - let result = panic::catch_unwind(move || f(world, params)); + let result = panic::catch_unwind(move || { + let _pctx = stdx::panic_context::enter(panic_context); + f(world, params) + }); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), Err(_) => Task::Response(lsp_server::Response::new_err( @@ -175,10 +178,13 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn(intent, panic_context, { + self.global_state.task_pool.handle.spawn(intent, { let world = self.global_state.snapshot(); move || { - let result = panic::catch_unwind(move || f(world, params)); + let result = panic::catch_unwind(move || { + let _pctx = stdx::panic_context::enter(panic_context); + f(world, params) + }); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), Err(_) => Task::Retry(req), diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index b3623669cc6b3..09de6900c8fb5 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -291,15 +291,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } Ok(()) }; - state.task_pool.handle.spawn_with_sender( - stdx::thread::ThreadIntent::Worker, - "flycheck", - move |_| { - if let Err(e) = std::panic::catch_unwind(task) { - tracing::error!("flycheck task panicked: {e:?}") - } - }, - ); + state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { + if let Err(e) = std::panic::catch_unwind(task) { + tracing::error!("flycheck task panicked: {e:?}") + } + }); true } else { false diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 92d44eeee8941..19c49a23000ea 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -397,25 +397,19 @@ impl GlobalState { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); - self.task_pool.handle.spawn_with_sender( - stdx::thread::ThreadIntent::Worker, - "prime_caches", - { - let analysis = self.snapshot().analysis; - move |sender| { - sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); - let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { - let report = PrimeCachesProgress::Report(progress); - sender.send(Task::PrimeCaches(report)).unwrap(); - }); - sender - .send(Task::PrimeCaches(PrimeCachesProgress::End { - cancelled: res.is_err(), - })) - .unwrap(); - } - }, - ); + self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, { + let analysis = self.snapshot().analysis; + move |sender| { + sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); + let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { + let report = PrimeCachesProgress::Report(progress); + sender.send(Task::PrimeCaches(report)).unwrap(); + }); + sender + .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() })) + .unwrap(); + } + }); } fn update_status_or_notify(&mut self) { @@ -802,62 +796,56 @@ impl GlobalState { // Diagnostics are triggered by the user typing // so we run them on a latency sensitive thread. - self.task_pool.handle.spawn( - stdx::thread::ThreadIntent::LatencySensitive, - "publish_diagnostics", - move || { - let _p = profile::span("publish_diagnostics"); - let diagnostics = subscriptions - .into_iter() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - Some(( - file_id, - line_index, - snapshot - .analysis - .diagnostics( - &snapshot.config.diagnostics(), - ide::AssistResolveStrategy::None, - file_id, - ) - .ok()?, - )) - }) - .map(|(file_id, line_index, it)| { - ( - file_id, - it.into_iter() - .map(move |d| lsp_types::Diagnostic { - range: crate::to_proto::range(&line_index, d.range), - severity: Some(crate::to_proto::diagnostic_severity( - d.severity, - )), - code: Some(lsp_types::NumberOrString::String( - d.code.as_str().to_string(), - )), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { - Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) - } else { - None - }, - data: None, - }) - .collect::>(), - ) - }); - Task::Diagnostics(diagnostics.collect()) - }, - ); + self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || { + let _p = profile::span("publish_diagnostics"); + let diagnostics = subscriptions + .into_iter() + .filter_map(|file_id| { + let line_index = snapshot.file_line_index(file_id).ok()?; + Some(( + file_id, + line_index, + snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()?, + )) + }) + .map(|(file_id, line_index, it)| { + ( + file_id, + it.into_iter() + .map(move |d| lsp_types::Diagnostic { + range: crate::to_proto::range(&line_index, d.range), + severity: Some(crate::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String( + d.code.as_str().to_string(), + )), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&format!( + "https://rust-analyzer.github.io/manual.html#{}", + d.code.as_str() + )) + .unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: if d.unused { + Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) + } else { + None + }, + data: None, + }) + .collect::>(), + ) + }); + Task::Diagnostics(diagnostics.collect()) + }); } } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 5911e24d999c2..6e8c8ea91a1b3 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -185,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, "fetch_workspaces", { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -260,25 +260,19 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender( - ThreadIntent::Worker, - "fetch_build_data", - move |sender| { - sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { + sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); - let progress = { - let sender = sender.clone(); - move |msg| { - sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() - } - }; - let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); + let progress = { + let sender = sender.clone(); + move |msg| { + sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() + } + }; + let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); - sender - .send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))) - .unwrap(); - }, - ); + sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap(); + }); } pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { @@ -286,54 +280,50 @@ impl GlobalState { let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); - self.task_pool.handle.spawn_with_sender( - ThreadIntent::Worker, - "fetch_proc_macros", - move |sender| { - sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); - - let dummy_replacements = &dummy_replacements; - let progress = { - let sender = sender.clone(); - &move |msg| { - sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() - } - }; + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); - let mut res = FxHashMap::default(); - let chain = proc_macro_clients - .iter() - .map(|res| res.as_ref().map_err(|e| e.to_string())) - .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into()))); - for (client, paths) in chain.zip(paths) { - res.extend(paths.into_iter().map(move |(crate_id, res)| { - ( - crate_id, - res.map_or_else( - |_| Err("proc macro crate is missing dylib".to_owned()), - |(crate_name, path)| { - progress(path.display().to_string()); - client.as_ref().map_err(Clone::clone).and_then(|client| { - load_proc_macro( - client, - &path, - crate_name - .as_deref() - .and_then(|crate_name| { - dummy_replacements.get(crate_name).map(|v| &**v) - }) - .unwrap_or_default(), - ) - }) - }, - ), - ) - })); + let dummy_replacements = &dummy_replacements; + let progress = { + let sender = sender.clone(); + &move |msg| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() } + }; + + let mut res = FxHashMap::default(); + let chain = proc_macro_clients + .iter() + .map(|res| res.as_ref().map_err(|e| e.to_string())) + .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into()))); + for (client, paths) in chain.zip(paths) { + res.extend(paths.into_iter().map(move |(crate_id, res)| { + ( + crate_id, + res.map_or_else( + |_| Err("proc macro crate is missing dylib".to_owned()), + |(crate_name, path)| { + progress(path.display().to_string()); + client.as_ref().map_err(Clone::clone).and_then(|client| { + load_proc_macro( + client, + &path, + crate_name + .as_deref() + .and_then(|crate_name| { + dummy_replacements.get(crate_name).map(|v| &**v) + }) + .unwrap_or_default(), + ) + }) + }, + ), + ) + })); + } - sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); - }, - ); + sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); + }); } pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) { diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index 823210980f2c5..a5a10e86914e0 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -14,41 +14,25 @@ impl TaskPool { TaskPool { sender, pool: Pool::new(threads) } } - pub(crate) fn spawn( - &mut self, - intent: ThreadIntent, - panic_context: impl Into, - task: F, - ) where + pub(crate) fn spawn(&mut self, intent: ThreadIntent, task: F) + where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - let panic_context = panic_context.into(); self.pool.spawn(intent, { let sender = self.sender.clone(); - move || { - let _pctx = stdx::panic_context::enter(panic_context); - sender.send(task()).unwrap() - } + move || sender.send(task()).unwrap() }) } - pub(crate) fn spawn_with_sender( - &mut self, - intent: ThreadIntent, - panic_context: impl Into, - task: F, - ) where + pub(crate) fn spawn_with_sender(&mut self, intent: ThreadIntent, task: F) + where F: FnOnce(Sender) + Send + 'static, T: Send + 'static, { - let panic_context = panic_context.into(); self.pool.spawn(intent, { let sender = self.sender.clone(); - move || { - let _pctx = stdx::panic_context::enter(panic_context); - task(sender) - } + move || task(sender) }) } From cce0b52e7bc7ca2b95fe9f95c8528cd87e787e33 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 26 May 2023 01:23:55 -0700 Subject: [PATCH 660/806] Add a codegen test for manually swapping a small `Copy` type To confirm we're not just helping `mem::swap` --- tests/codegen/swap-small-types.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 03e2a2327fc4c..6289d7af3a08c 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -8,10 +8,30 @@ use std::mem::swap; type RGB48 = [u16; 3]; +// CHECK-LABEL: @swap_rgb48_manually( +#[no_mangle] +pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { + // CHECK-NOT: alloca + // CHECK: %temp = alloca [3 x i16] + // CHECK-NOT: alloca + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %temp, {{.+}} %x, {{.+}} 6, {{.+}}) + // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %x, {{.+}} %y, {{.+}} 6, {{.+}}) + // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %y, {{.+}} %temp, {{.+}} 6, {{.+}}) + // CHECK-NOT: call void @llvm.memcpy + + let temp = *x; + *x = *y; + *y = temp; +} + // CHECK-LABEL: @swap_rgb48 #[no_mangle] pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) { // FIXME MIR inlining messes up LLVM optimizations. + // If these checks start failing, please update this test. + // CHECK: alloca [3 x i16] + // CHECK: call void @llvm.memcpy // WOULD-CHECK-NOT: alloca // WOULD-CHECK: load i48 // WOULD-CHECK: store i48 From e1b020df9f72eab7e8b3e38a5263ddda54ce18e1 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 26 May 2023 03:32:22 -0700 Subject: [PATCH 661/806] Use `load`-`store` instead of `memcpy` for short integer arrays --- compiler/rustc_codegen_llvm/src/type_.rs | 3 ++ compiler/rustc_codegen_llvm/src/type_of.rs | 33 +++++++++++++++++ compiler/rustc_codegen_ssa/src/base.rs | 14 +++++++- .../rustc_codegen_ssa/src/traits/type_.rs | 22 ++++++++++++ tests/codegen/array-codegen.rs | 35 +++++++++++++++++++ tests/codegen/mem-replace-simple-type.rs | 11 ++++++ tests/codegen/swap-simd-types.rs | 9 +++++ tests/codegen/swap-small-types.rs | 25 ++++++------- 8 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 tests/codegen/array-codegen.rs diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index d3fad5699c800..4ffa2b9c6a39d 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -288,6 +288,9 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn reg_backend_type(&self, ty: &Reg) -> &'ll Type { ty.llvm_type(self) } + fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { + layout.scalar_copy_llvm_type(self) + } } impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index a493c9c0548e9..3339e4e07edd3 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -6,6 +6,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_target::abi::HasDataLayout; use rustc_target::abi::{Abi, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants}; @@ -192,6 +193,7 @@ pub trait LayoutLlvmExt<'tcx> { ) -> &'a Type; fn llvm_field_index<'a>(&self, cx: &CodegenCx<'a, 'tcx>, index: usize) -> u64; fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option; + fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type>; } impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { @@ -414,4 +416,35 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result } + + fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type> { + debug_assert!(self.is_sized()); + + // FIXME: this is a fairly arbitrary choice, but 128 bits on WASM + // (matching the 128-bit SIMD types proposal) and 256 bits on x64 + // (like AVX2 registers) seems at least like a tolerable starting point. + let threshold = cx.data_layout().pointer_size * 4; + if self.layout.size() > threshold { + return None; + } + + // Vectors, even for non-power-of-two sizes, have the same layout as + // arrays but don't count as aggregate types + if let FieldsShape::Array { count, .. } = self.layout.fields() + && let element = self.field(cx, 0) + && element.ty.is_integral() + { + // `cx.type_ix(bits)` is tempting here, but while that works great + // for things that *stay* as memory-to-memory copies, it also ends + // up suppressing vectorization as it introduces shifts when it + // extracts all the individual values. + + let ety = element.llvm_type(cx); + return Some(cx.type_vector(ety, *count)); + } + + // FIXME: The above only handled integer arrays; surely more things + // would also be possible. Be careful about provenance, though! + None + } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 242d209b68425..dc4a28c866ff3 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -380,7 +380,19 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return; } - bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + if flags == MemFlags::empty() + && let Some(bty) = bx.cx().scalar_copy_backend_type(layout) + { + // I look forward to only supporting opaque pointers + let pty = bx.type_ptr_to(bty); + let src = bx.pointercast(src, pty); + let dst = bx.pointercast(dst, pty); + + let temp = bx.load(bty, src, src_align); + bx.store(temp, dst, dst_align); + } else { + bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + } } pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 36d9864221bcb..e64417e1a4a69 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -126,6 +126,28 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { index: usize, immediate: bool, ) -> Self::Type; + + /// A type that can be used in a [`super::BuilderMethods::load`] + + /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, + /// such as a MIR `*_0 = *_1`. + /// + /// It's always legal to return `None` here, as the provided impl does, + /// in which case callers should use [`super::BuilderMethods::memcpy`] + /// instead of the `load`+`store` pair. + /// + /// This can be helpful for things like arrays, where the LLVM backend type + /// `[3 x i16]` optimizes to three separate loads and stores, but it can + /// instead be copied via an `i48` that stays as the single `load`+`store`. + /// (As of 2023-05 LLVM cannot necessarily optimize away a `memcpy` in these + /// cases, due to `poison` handling, but in codegen we have more information + /// about the type invariants, so can emit something better instead.) + /// + /// This *should* return `None` for particularly-large types, where leaving + /// the `memcpy` may well be important to avoid code size explosion. + fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { + let _ = layout; + None + } } // For backends that support CFI using type membership (i.e., testing whether a given pointer is diff --git a/tests/codegen/array-codegen.rs b/tests/codegen/array-codegen.rs new file mode 100644 index 0000000000000..98488eb92ee02 --- /dev/null +++ b/tests/codegen/array-codegen.rs @@ -0,0 +1,35 @@ +// compile-flags: -O -C no-prepopulate-passes +// min-llvm-version: 15.0 (for opaque pointers) + +#![crate_type = "lib"] + +// CHECK-LABEL: @array_load +#[no_mangle] +pub fn array_load(a: &[u8; 4]) -> [u8; 4] { + // CHECK: %0 = alloca [4 x i8], align 1 + // CHECK: %[[TEMP1:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK: store <4 x i8> %[[TEMP1]], ptr %0, align 1 + // CHECK: %[[TEMP2:.+]] = load i32, ptr %0, align 1 + // CHECK: ret i32 %[[TEMP2]] + *a +} + +// CHECK-LABEL: @array_store +#[no_mangle] +pub fn array_store(a: [u8; 4], p: &mut [u8; 4]) { + // CHECK: %a = alloca [4 x i8] + // CHECK: %[[TEMP:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK-NEXT: store <4 x i8> %[[TEMP]], ptr %p, align 1 + *p = a; +} + +// CHECK-LABEL: @array_copy +#[no_mangle] +pub fn array_copy(a: &[u8; 4], p: &mut [u8; 4]) { + // CHECK: %[[LOCAL:.+]] = alloca [4 x i8], align 1 + // CHECK: %[[TEMP1:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK: store <4 x i8> %[[TEMP1]], ptr %[[LOCAL]], align 1 + // CHECK: %[[TEMP2:.+]] = load <4 x i8>, ptr %[[LOCAL]], align 1 + // CHECK: store <4 x i8> %[[TEMP2]], ptr %p, align 1 + *p = *a; +} diff --git a/tests/codegen/mem-replace-simple-type.rs b/tests/codegen/mem-replace-simple-type.rs index 4253ef1366604..6151177de15b2 100644 --- a/tests/codegen/mem-replace-simple-type.rs +++ b/tests/codegen/mem-replace-simple-type.rs @@ -32,3 +32,14 @@ pub fn replace_ref_str<'a>(r: &mut &'a str, v: &'a str) -> &'a str { // CHECK: ret { ptr, i64 } %[[P2]] std::mem::replace(r, v) } + +#[no_mangle] +// CHECK-LABEL: @replace_short_array( +pub fn replace_short_array(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] { + // CHECK-NOT: alloca + // CHECK: %[[R:.+]] = load <3 x i32>, ptr %r, align 4 + // CHECK: store <3 x i32> %[[R]], ptr %0 + // CHECK: %[[V:.+]] = load <3 x i32>, ptr %v, align 4 + // CHECK: store <3 x i32> %[[V]], ptr %r + std::mem::replace(r, v) +} diff --git a/tests/codegen/swap-simd-types.rs b/tests/codegen/swap-simd-types.rs index c90b277eb4487..3472a42b0e65e 100644 --- a/tests/codegen/swap-simd-types.rs +++ b/tests/codegen/swap-simd-types.rs @@ -30,3 +30,12 @@ pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) { x.swap_with_slice(y); } } + +// CHECK-LABEL: @swap_bytes32 +#[no_mangle] +pub fn swap_bytes32(x: &mut [u8; 32], y: &mut [u8; 32]) { +// CHECK-NOT: alloca +// CHECK: load <32 x i8>{{.+}}align 1 +// CHECK: store <32 x i8>{{.+}}align 1 + swap(x, y) +} diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 6289d7af3a08c..419645a3fc6bc 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -1,4 +1,4 @@ -// compile-flags: -O +// compile-flags: -O -Z merge-functions=disabled // only-x86_64 // ignore-debug: the debug assertions get in the way @@ -12,13 +12,10 @@ type RGB48 = [u16; 3]; #[no_mangle] pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { // CHECK-NOT: alloca - // CHECK: %temp = alloca [3 x i16] - // CHECK-NOT: alloca - // CHECK-NOT: call void @llvm.memcpy - // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %temp, {{.+}} %x, {{.+}} 6, {{.+}}) - // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %x, {{.+}} %y, {{.+}} 6, {{.+}}) - // CHECK: call void @llvm.memcpy.{{.+}}({{.+}} %y, {{.+}} %temp, {{.+}} 6, {{.+}}) - // CHECK-NOT: call void @llvm.memcpy + // CHECK: %[[TEMP0:.+]] = load <3 x i16>, ptr %x, align 2 + // CHECK: %[[TEMP1:.+]] = load <3 x i16>, ptr %y, align 2 + // CHECK: store <3 x i16> %[[TEMP1]], ptr %x, align 2 + // CHECK: store <3 x i16> %[[TEMP0]], ptr %y, align 2 let temp = *x; *x = *y; @@ -28,13 +25,11 @@ pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { // CHECK-LABEL: @swap_rgb48 #[no_mangle] pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) { - // FIXME MIR inlining messes up LLVM optimizations. - // If these checks start failing, please update this test. - // CHECK: alloca [3 x i16] - // CHECK: call void @llvm.memcpy -// WOULD-CHECK-NOT: alloca -// WOULD-CHECK: load i48 -// WOULD-CHECK: store i48 + // CHECK-NOT: alloca + // CHECK: load <3 x i16> + // CHECK: load <3 x i16> + // CHECK: store <3 x i16> + // CHECK: store <3 x i16> swap(x, y) } From f9a9e40c0a5e5b150a8c7339ef33ddc7ddce325c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 4 Jun 2023 10:02:11 +0200 Subject: [PATCH 662/806] Update builtin attribute list --- Cargo.toml | 1 + crates/hir-def/src/attr/builtin.rs | 166 ++++++++++++++++---------- crates/rust-analyzer/src/main_loop.rs | 1 + crates/test-utils/src/minicore.rs | 9 ++ 4 files changed, 115 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 513a769ca177d..3050cf764a4c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = ["xtask/", "lib/*", "crates/*"] exclude = ["crates/proc-macro-test/imp"] +resolver = "2" [workspace.package] rust-version = "1.66" diff --git a/crates/hir-def/src/attr/builtin.rs b/crates/hir-def/src/attr/builtin.rs index f7c1e683d0d20..b232cb12bc871 100644 --- a/crates/hir-def/src/attr/builtin.rs +++ b/crates/hir-def/src/attr/builtin.rs @@ -2,7 +2,7 @@ //! //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. //! -//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7. +//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6. //! //! The macros were adjusted to only expand to the attribute name, since that is all we need to do //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to @@ -108,7 +108,7 @@ macro_rules! experimental { }; } -/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc. +/// Attributes that have a special meaning to rustc or rustdoc. #[rustfmt::skip] pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== @@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing), ungated!( should_panic, Normal, - template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing, + template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing, ), // FIXME(Centril): This can be used on stable but shouldn't. ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing), @@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Lints: ungated!( - warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!( - allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), gated!( expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk, lint_reasons, experimental!(expect) ), ungated!( - forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!( - deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing), gated!( @@ -181,16 +185,17 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // ABI, linking, symbols, and FFI ungated!( link, Normal, - template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), + template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#), DuplicatesOk, ), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_link, Normal, template!(Word), WarnFollowing), - ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true), ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true), + ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding), // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), @@ -205,6 +210,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Entry point: + gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)), ungated!(start, Normal, template!(Word), WarnFollowing), ungated!(no_start, CrateLevel, template!(Word), WarnFollowing), ungated!(no_main, CrateLevel, template!(Word), WarnFollowing), @@ -226,11 +232,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true), ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), - ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), + ungated!( + target_feature, Normal, template!(List: r#"enable = "name""#), + DuplicatesOk, @only_local: true, + ), ungated!(track_caller, Normal, template!(Word), WarnFollowing), + ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding), gated!( no_sanitize, Normal, - template!(List: "address, memory, thread"), DuplicatesOk, + template!(List: "address, kcfi, memory, thread"), DuplicatesOk, experimental!(no_sanitize) ), gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)), @@ -239,25 +249,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk ), + // Debugging + ungated!( + debugger_visualizer, Normal, + template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk + ), + // ========================================================================== // Unstable attributes: // ========================================================================== - // RFC #3191: #[debugger_visualizer] support - gated!( - debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), - DuplicatesOk, experimental!(debugger_visualizer) - ), - // Linking: - gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)), gated!( - link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib, - experimental!(link_ordinal) + naked, Normal, template!(Word), WarnFollowing, @only_local: true, + naked_functions, experimental!(naked) ), // Plugins: - // XXX Modified for use in rust-analyzer // BuiltinAttribute { // name: sym::plugin, // only_local: false, @@ -274,10 +282,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // cfg_fn!(plugin) // ), // }, - BuiltinAttribute { - name: "plugin", - template: template!(List: "name"), - }, // Testing: gated!( @@ -286,7 +290,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC #1268 gated!( - marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker) + marker, Normal, template!(Word), WarnFollowing, @only_local: true, + marker_trait_attr, experimental!(marker) ), gated!( thread_local, Normal, template!(Word), WarnFollowing, @@ -298,21 +303,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute, experimental!(optimize), ), - // RFC 2867 - gated!( - instruction_set, Normal, template!(List: "set"), ErrorPreceding, - isa_attribute, experimental!(instruction_set) - ), gated!( ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice) ), gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)), gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)), - gated!( - register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk, - experimental!(register_attr), - ), gated!( register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk, experimental!(register_tool), @@ -325,7 +321,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // RFC 2632 gated!( const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl, - "`const` is a temporary placeholder for marking a trait that is suitable for `const` \ + "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \ `impls` and all default bodies as `const`, which may be removed or renamed in the \ future." ), @@ -335,22 +331,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ experimental!(deprecated_safe), ), + // `#[collapse_debuginfo]` + gated!( + collapse_debuginfo, Normal, template!(Word), WarnFollowing, + experimental!(collapse_debuginfo) + ), + + // RFC 2397 + gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)), + + // `#[cfi_encoding = ""]` + gated!( + cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, + experimental!(cfi_encoding) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== - ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk), + ungated!( + feature, CrateLevel, + template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true, + ), // DuplicatesOk since it has its own validation ungated!( - stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, + stable, Normal, + template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true, ), ungated!( unstable, Normal, template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, ), ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), - ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), - ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), + ungated!( + rustc_const_stable, Normal, + template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true, + ), + ungated!( + rustc_default_body_unstable, Normal, + template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk + ), gated!( allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, "allow_internal_unstable side-steps feature gating and stability checks", @@ -364,6 +385,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), + ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk), + rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths"), // ========================================================================== // Internal attributes: Type system related: @@ -381,10 +406,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - gated!( - alloc_error_handler, Normal, template!(Word), WarnFollowing, - experimental!(alloc_error_handler) - ), + rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, experimental!(default_lib_allocator), @@ -465,6 +489,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: @@ -508,18 +538,25 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ "language items are subject to change", ), rustc_attr!( - rustc_pass_by_value, Normal, - template!(Word), ErrorFollowing, + rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." ), + rustc_attr!( + rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true, + "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver." + ), rustc_attr!( rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false, + "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" + ), rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ @@ -531,24 +568,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ and it is only intended to be used in `alloc`." ), - // modified for r-a - // BuiltinAttribute { - // name: sym::rustc_diagnostic_item, - // // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. - // only_local: false, - // type_: Normal, - // template: template!(NameValueStr: "name"), - // duplicates: ErrorFollowing, - // gate: Gated( - // Stability::Unstable, - // sym::rustc_attrs, - // "diagnostic items compiler internal support for linting", - // cfg_fn!(rustc_attrs), - // ), - // }, BuiltinAttribute { + // name: sym::rustc_diagnostic_item, name: "rustc_diagnostic_item", + // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. + // only_local: false, + // type_: Normal, template: template!(NameValueStr: "name"), + // duplicates: ErrorFollowing, + // gate: Gated( + // Stability::Unstable, + // sym::rustc_attrs, + // "diagnostic items compiler internal support for linting", + // cfg_fn!(rustc_attrs), + // ), }, gated!( // Used in resolve: @@ -572,7 +605,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ for reserving for `for From for T` impl" ), rustc_attr!( - rustc_test_marker, Normal, template!(Word), WarnFollowing, + rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, "the `#[rustc_test_marker]` attribute is used internally to track tests", ), rustc_attr!( @@ -598,11 +631,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ definition of a trait, it's currently in experimental form and should be changed before \ being exposed outside of the std" ), + rustc_attr!( + rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, + r#"`rustc_doc_primitive` is a rustc internal attribute"#, + ), // ========================================================================== // Internal attributes, Testing: // ========================================================================== + rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), @@ -643,6 +681,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), + gated!( + custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), + ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 19c49a23000ea..12bc638929d66 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -798,6 +798,7 @@ impl GlobalState { // so we run them on a latency sensitive thread. self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || { let _p = profile::span("publish_diagnostics"); + let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned()); let diagnostics = subscriptions .into_iter() .filter_map(|file_id| { diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 3ca31cce5649e..c79b4e966d67e 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -29,6 +29,7 @@ //! future: pin //! generator: pin //! hash: +//! include: //! index: sized //! infallible: //! iterator: option @@ -1274,6 +1275,14 @@ mod macros { } } // endregion:derive + + // region:include + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; + } + // endregion:include } // region:non_zero From 75b557a2c416f997385a886b39e7ef7625073f5a Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Sun, 4 Jun 2023 10:38:14 +0200 Subject: [PATCH 663/806] Fix type-inference regression in #112225 The type inference of argument-position closures and async blocks regressed in 1.70 as the evaluation order of async blocks changed, as they are not implicitly wrapped in an identity-function anymore. Fixes #112225 by making sure the evaluation order stays the same as it used to. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 11 +++++++++- tests/ui/async-await/issues/issue-112225-1.rs | 18 +++++++++++++++++ tests/ui/async-await/issues/issue-112225-2.rs | 20 +++++++++++++++++++ .../async-await/issues/issue-112225-2.stderr | 17 ++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/ui/async-await/issues/issue-112225-1.rs create mode 100644 tests/ui/async-await/issues/issue-112225-2.rs create mode 100644 tests/ui/async-await/issues/issue-112225-2.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 72c42f8e78952..eba5c829e396d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -362,7 +362,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); + // For this check, we do *not* want to treat async generator closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See . + let is_closure = if let ExprKind::Closure(closure) = arg.kind { + !tcx.generator_is_async(closure.def_id.to_def_id()) + } else { + false + }; if is_closure != check_closures { continue; } diff --git a/tests/ui/async-await/issues/issue-112225-1.rs b/tests/ui/async-await/issues/issue-112225-1.rs new file mode 100644 index 0000000000000..e28cbee214e1a --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-1.rs @@ -0,0 +1,18 @@ +// check-pass +// edition:2021 + +use core::future::Future; + +fn main() { + do_async(async { (0,) }, { + // closure must be inside block + |info| println!("{:?}", info.0) + }); +} + +fn do_async(_tokio_fut: Fut, _glib_closure: F) +where + Fut: Future, + F: FnOnce(R), +{ +} diff --git a/tests/ui/async-await/issues/issue-112225-2.rs b/tests/ui/async-await/issues/issue-112225-2.rs new file mode 100644 index 0000000000000..50fa1a79b6beb --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-2.rs @@ -0,0 +1,20 @@ +// edition:2021 + +// With the current compiler logic, we cannot have both the `112225-1` case, +// and this `112225-2` case working, as the type inference depends on the evaluation +// order, and there is some explicit ordering going on. +// See the `check_closures` part in `FnCtxt::check_argument_types`. +// The `112225-1` case was a regression in real world code, whereas the `112225-2` +// case never used to work prior to 1.70. + +use core::future::Future; + +fn main() { + let x = Default::default(); + //~^ ERROR: type annotations needed + do_async( + async { x.0; }, + { || { let _: &(i32,) = &x; } }, + ); +} +fn do_async(_fut: Fut, _val: T, ) {} diff --git a/tests/ui/async-await/issues/issue-112225-2.stderr b/tests/ui/async-await/issues/issue-112225-2.stderr new file mode 100644 index 0000000000000..5926a4f3995ad --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-2.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/issue-112225-2.rs:13:9 + | +LL | let x = Default::default(); + | ^ +... +LL | async { x.0; }, + | - type must be known at this point + | +help: consider giving `x` an explicit type + | +LL | let x: /* Type */ = Default::default(); + | ++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From 71f3e4b08ca0f4dc63fa6a58f921e8e1addf404d Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 4 Jun 2023 12:39:36 +0330 Subject: [PATCH 664/806] Detect "bound more than once" error and suppress `need-mut` for it. --- crates/hir-def/src/body/lower.rs | 62 ++++++++++++++++--- crates/hir-def/src/hir.rs | 11 ++++ crates/hir/src/lib.rs | 6 +- .../src/handlers/mutability_errors.rs | 13 ++++ 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index d8ecde71025a0..7b88e525bf184 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -30,9 +30,9 @@ use crate::{ db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr, - ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, Pat, PatId, - RecordFieldPat, RecordLitField, Statement, + dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, + ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, + Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -141,6 +141,8 @@ impl RibKind { #[derive(Debug, Default)] struct BindingList { map: FxHashMap, + is_used: FxHashMap, + reject_new: bool, } impl BindingList { @@ -150,7 +152,27 @@ impl BindingList { name: Name, mode: BindingAnnotation, ) -> BindingId { - *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)) + let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)); + if ec.body.bindings[id].mode != mode { + ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); + } + self.check_is_used(ec, id); + id + } + + fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) { + match self.is_used.get(&id) { + None => { + if self.reject_new { + ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); + } + } + Some(true) => { + ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); + } + Some(false) => {} + } + self.is_used.insert(id, true); } } @@ -1208,9 +1230,34 @@ impl ExprCollector<'_> { p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); path.map(Pat::Path).unwrap_or(Pat::Missing) } - ast::Pat::OrPat(p) => { - let pats = p.pats().map(|p| self.collect_pat(p, binding_list)).collect(); - Pat::Or(pats) + ast::Pat::OrPat(p) => 'b: { + let prev_is_used = mem::take(&mut binding_list.is_used); + let prev_reject_new = mem::take(&mut binding_list.reject_new); + let mut pats = Vec::with_capacity(p.pats().count()); + let mut it = p.pats(); + let Some(first) = it.next() else { + break 'b Pat::Or(Box::new([])); + }; + pats.push(self.collect_pat(first, binding_list)); + binding_list.reject_new = true; + for rest in it { + for (_, x) in binding_list.is_used.iter_mut() { + *x = false; + } + pats.push(self.collect_pat(rest, binding_list)); + for (&id, &x) in binding_list.is_used.iter() { + if !x { + self.body.bindings[id].problems = + Some(BindingProblems::NotBoundAcrossAll); + } + } + } + binding_list.reject_new = prev_reject_new; + let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used); + for (id, _) in current_is_used.into_iter() { + binding_list.check_is_used(self, id); + } + Pat::Or(pats.into()) } ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list), ast::Pat::TuplePat(p) => { @@ -1499,6 +1546,7 @@ impl ExprCollector<'_> { mode, definitions: SmallVec::new(), owner: self.current_binding_owner, + problems: None, }) } diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 8102efdba3089..4ad8a7aa8eb12 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -486,6 +486,16 @@ impl BindingAnnotation { } } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum BindingProblems { + /// https://doc.rust-lang.org/stable/error_codes/E0416.html + BoundMoreThanOnce, + /// https://doc.rust-lang.org/stable/error_codes/E0409.html + BoundInconsistently, + /// https://doc.rust-lang.org/stable/error_codes/E0408.html + NotBoundAcrossAll, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Binding { pub name: Name, @@ -494,6 +504,7 @@ pub struct Binding { /// Id of the closure/generator that owns this binding. If it is owned by the /// top level expression, this field would be `None`. pub owner: Option, + pub problems: Option, } impl Binding { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 2db4b483b69d4..5926d8654210d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1643,7 +1643,11 @@ impl DefWithBody { ) } let mol = &borrowck_result.mutability_of_locals; - for (binding_id, _) in hir_body.bindings.iter() { + for (binding_id, binding_data) in hir_body.bindings.iter() { + if binding_data.problems.is_some() { + // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`. + continue; + } let Some(&local) = mir_body.binding_locals.get(binding_id) else { continue; }; diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 743ef0c6f5290..b95c8c573b506 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -620,6 +620,19 @@ fn f((x, y): (i32, i32)) { ); } + #[test] + fn no_diagnostics_in_case_of_multiple_bounds() { + check_diagnostics( + r#" +fn f() { + let (b, a, b) = (2, 3, 5); + a = 8; + //^^^^^ 💡 error: cannot mutate immutable variable `a` +} +"#, + ); + } + #[test] fn for_loop() { check_diagnostics( From 275afd6e79f4e23d870a8017c805f786e400af72 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 4 Jun 2023 19:38:47 +0900 Subject: [PATCH 665/806] fix: consider outer binders when folding captured items' type --- crates/hir-ty/src/infer/closure.rs | 17 +++++---------- crates/hir-ty/src/mir/eval/tests.rs | 34 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 754ac88bb50ce..e98905f4eee53 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -5,7 +5,7 @@ use std::{cmp, collections::HashMap, convert::Infallible, mem}; use chalk_ir::{ cast::Cast, fold::{FallibleTypeFolder, TypeFoldable}, - AliasEq, AliasTy, BoundVar, ConstData, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, + AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, }; use hir_def::{ data::adt::VariantData, @@ -26,8 +26,8 @@ use crate::{ static_lifetime, to_chalk_trait_id, traits::FnTrait, utils::{self, generics, Generics}, - Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, ConstValue, DynTy, - FnPointer, FnSig, Interner, Substitution, Ty, TyExt, + Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, + Interner, Substitution, Ty, TyExt, }; use super::{Expectation, InferenceContext}; @@ -266,24 +266,19 @@ impl CapturedItemWithoutTy { let Some(idx) = self.generics.param_idx(x) else { return Err(()); }; - Ok(ConstData { - ty, - value: ConstValue::BoundVar(BoundVar::new(outer_binder, idx)), - } - .intern(Interner)) + Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) } fn try_fold_free_placeholder_ty( &mut self, idx: chalk_ir::PlaceholderIndex, - _outer_binder: DebruijnIndex, + outer_binder: DebruijnIndex, ) -> std::result::Result { let x = from_placeholder_idx(self.db, idx); let Some(idx) = self.generics.param_idx(x) else { return Err(()); }; - Ok(TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx)) - .intern(Interner)) + Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) } } let g_def = match owner { diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index dabc76ba15bf4..ca4268b8fb00c 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -640,3 +640,37 @@ fn main() { "#, ); } + +#[test] +fn regression_14966() { + check_pass( + r#" +//- minicore: fn, copy, coerce_unsized +trait A { + fn a(&self) {} +} +impl A<()> for () {} + +struct B; +impl B { + pub fn b(s: &dyn A) -> Self { + B + } +} +struct C; +impl C { + fn c(a: &dyn A) -> Self { + let mut c = C; + let b = B::b(a); + c.d(|| a.a()); + c + } + fn d(&mut self, f: impl FnOnce()) {} +} + +fn main() { + C::c(&()); +} +"#, + ); +} From a3789eabc9281d29dce3c4199c971f0490074dfe Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 4 Jun 2023 19:39:49 +0900 Subject: [PATCH 666/806] Minor refactorings - use `DefWithBodyId::as_generic_def_id()` - add comments on `InferenceResult` invariant - move local helper function to bottom to comply with style guide --- crates/hir-ty/src/infer.rs | 6 +++ crates/hir-ty/src/infer/closure.rs | 45 ++++++++++------------- crates/hir-ty/src/mir/monomorphization.rs | 24 ++---------- 3 files changed, 29 insertions(+), 46 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index fa81fe39aa116..ccfa626b5fdbc 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -367,6 +367,10 @@ pub enum PointerCast { } /// The result of type inference: A mapping from expressions and patterns to types. +/// +/// When you add a field that stores types (including `Substitution` and the like), don't forget +/// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must +/// not appear in the final inference result. #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct InferenceResult { /// For each method call expr, records the function it resolves to. @@ -575,6 +579,8 @@ impl<'a> InferenceContext<'a> { // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. pub(crate) fn resolve_all(self) -> InferenceResult { + // NOTE: `InferenceResult::closure_info` is `resolve_completely()`'d during + // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically). let InferenceContext { mut table, mut result, .. } = self; table.fallback_if_possible(); diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index e98905f4eee53..23189f383e0e6 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -236,6 +236,24 @@ pub(crate) struct CapturedItemWithoutTy { impl CapturedItemWithoutTy { fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { + let ty = self.place.ty(ctx).clone(); + let ty = match &self.kind { + CaptureKind::ByValue => ty, + CaptureKind::ByRef(bk) => { + let m = match bk { + BorrowKind::Mut { .. } => Mutability::Mut, + _ => Mutability::Not, + }; + TyKind::Ref(m, static_lifetime(), ty).intern(Interner) + } + }; + return CapturedItem { + place: self.place, + kind: self.kind, + span: self.span, + ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty), + }; + fn replace_placeholder_with_binder( db: &dyn HirDatabase, owner: DefWithBodyId, @@ -281,36 +299,13 @@ impl CapturedItemWithoutTy { Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) } } - let g_def = match owner { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, - DefWithBodyId::ConstId(f) => Some(f.into()), - DefWithBodyId::VariantId(f) => Some(f.into()), - }; - let Some(generics) = g_def.map(|g_def| generics(db.upcast(), g_def)) else { + let Some(generic_def) = owner.as_generic_def_id() else { return Binders::empty(Interner, ty); }; - let filler = &mut Filler { db, generics }; + let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) }; let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); make_binders(db, &filler.generics, result) } - let ty = self.place.ty(ctx).clone(); - let ty = match &self.kind { - CaptureKind::ByValue => ty, - CaptureKind::ByRef(bk) => { - let m = match bk { - BorrowKind::Mut { .. } => Mutability::Mut, - _ => Mutability::Not, - }; - TyKind::Ref(m, static_lifetime(), ty).intern(Interner) - } - }; - CapturedItem { - place: self.place, - kind: self.kind, - span: self.span, - ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty), - } } } diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index b17ac58365699..ce3f7a8e51026 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -303,13 +303,7 @@ pub fn monomorphized_mir_body_query( subst: Substitution, trait_env: Arc, ) -> Result, MirLowerError> { - let g_def = match owner { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, - DefWithBodyId::ConstId(f) => Some(f.into()), - DefWithBodyId::VariantId(f) => Some(f.into()), - }; - let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; let body = db.mir_body(owner)?; let mut body = (*body).clone(); @@ -334,13 +328,7 @@ pub fn monomorphized_mir_body_for_closure_query( trait_env: Arc, ) -> Result, MirLowerError> { let (owner, _) = db.lookup_intern_closure(closure.into()); - let g_def = match owner { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, - DefWithBodyId::ConstId(f) => Some(f.into()), - DefWithBodyId::VariantId(f) => Some(f.into()), - }; - let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); @@ -356,13 +344,7 @@ pub fn monomorphize_mir_body_bad( trait_env: Arc, ) -> Result { let owner = body.owner; - let g_def = match owner { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, - DefWithBodyId::ConstId(f) => Some(f.into()), - DefWithBodyId::VariantId(f) => Some(f.into()), - }; - let generics = g_def.map(|g_def| generics(db.upcast(), g_def)); + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; filler.fill_body(&mut body)?; Ok(body) From f549cacc1d3fb09fd682fa4e0c29d45e8bd179a3 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 4 Jun 2023 20:32:46 +0900 Subject: [PATCH 667/806] Destructure `InferenceResult` in `resolve_all()` so that whenever new fields are added we don't forget to handle them. --- crates/hir-ty/src/infer.rs | 48 +++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index ccfa626b5fdbc..80f32e96ee63f 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -579,9 +579,31 @@ impl<'a> InferenceContext<'a> { // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. pub(crate) fn resolve_all(self) -> InferenceResult { - // NOTE: `InferenceResult::closure_info` is `resolve_completely()`'d during - // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically). let InferenceContext { mut table, mut result, .. } = self; + // Destructure every single field so whenever new fields are added to `InferenceResult` we + // don't forget to handle them here. + let InferenceResult { + method_resolutions, + field_resolutions: _, + variant_resolutions: _, + assoc_resolutions, + diagnostics, + type_of_expr, + type_of_pat, + type_of_binding, + type_of_rpit, + type_of_for_iterator, + type_mismatches, + standard_types: _, + pat_adjustments, + binding_modes: _, + expr_adjustments, + // Types in `closure_info` have already been `resolve_completely()`'d during + // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically), so no need + // to resolve them here. + closure_info: _, + mutated_bindings_in_closure: _, + } = &mut result; table.fallback_if_possible(); @@ -590,26 +612,26 @@ impl<'a> InferenceContext<'a> { // make sure diverging type variables are marked as such table.propagate_diverging_flag(); - for ty in result.type_of_expr.values_mut() { + for ty in type_of_expr.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_pat.values_mut() { + for ty in type_of_pat.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_binding.values_mut() { + for ty in type_of_binding.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_rpit.values_mut() { + for ty in type_of_rpit.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_for_iterator.values_mut() { + for ty in type_of_for_iterator.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for mismatch in result.type_mismatches.values_mut() { + for mismatch in type_mismatches.values_mut() { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone()); } - result.diagnostics.retain_mut(|diagnostic| { + diagnostics.retain_mut(|diagnostic| { use InferenceDiagnostic::*; match diagnostic { ExpectedFunction { found: ty, .. } @@ -637,16 +659,16 @@ impl<'a> InferenceContext<'a> { } true }); - for (_, subst) in result.method_resolutions.values_mut() { + for (_, subst) in method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } - for (_, subst) in result.assoc_resolutions.values_mut() { + for (_, subst) in assoc_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } - for adjustment in result.expr_adjustments.values_mut().flatten() { + for adjustment in expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); } - for adjustment in result.pat_adjustments.values_mut().flatten() { + for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); } result From 0408af6453e2bf9850731db2963ec5fb68dccc91 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 4 Jun 2023 15:56:01 +0330 Subject: [PATCH 668/806] Fix `unused-mut` false positive for `Box` --- crates/hir-ty/src/mir/borrowck.rs | 43 ++++++++++----- .../src/handlers/mutability_errors.rs | 52 +++++++++++++++++++ 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 66af6658e81ad..a0ea1cc5ef7d9 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -78,7 +78,7 @@ pub fn borrowck_query( .map(|body| { let body = body?; Ok(BorrowckResult { - mutability_of_locals: mutability_of_locals(&body), + mutability_of_locals: mutability_of_locals(db, &body), moved_out_of_ref: moved_out_of_ref(db, &body), mir_body: body, }) @@ -186,10 +186,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec result } -fn is_place_direct(lvalue: &Place) -> bool { - !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref) -} - +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ProjectionCase { /// Projection is a local Direct, @@ -199,12 +196,14 @@ enum ProjectionCase { Indirect, } -fn place_case(lvalue: &Place) -> ProjectionCase { +fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { let mut is_part_of = false; - for proj in lvalue.projection.iter().rev() { + let mut ty = body.locals[lvalue.local].ty.clone(); + for proj in lvalue.projection.iter() { match proj { - ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect - ProjectionElem::ConstantIndex { .. } + ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw + ProjectionElem::Deref // It's direct in case of `Box` + | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Field(_) | ProjectionElem::TupleOrClosureField(_) @@ -213,6 +212,23 @@ fn place_case(lvalue: &Place) -> ProjectionCase { } ProjectionElem::OpaqueCast(_) => (), } + ty = proj.projected_ty( + ty, + db, + |c, subst, f| { + let (def, _) = db.lookup_intern_closure(c.into()); + let infer = db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + body.owner.module(db.upcast()).krate(), + ); } if is_part_of { ProjectionCase::DirectPart @@ -300,7 +316,10 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap ArenaMap { +fn mutability_of_locals( + db: &dyn HirDatabase, + body: &MirBody, +) -> ArenaMap { let mut result: ArenaMap = body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); let mut push_mut_span = |local, span| match &mut result[local] { @@ -313,7 +332,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap { for statement in &block.statements { match &statement.kind { StatementKind::Assign(place, value) => { - match place_case(place) { + match place_case(db, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { push_mut_span(place.local, statement.span); @@ -328,7 +347,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap { ProjectionCase::Indirect => (), } if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { - if is_place_direct(p) { + if place_case(db, body, p) != ProjectionCase::Indirect { push_mut_span(p.local, statement.span); } } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index b95c8c573b506..8795afc2d9739 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -993,6 +993,58 @@ fn f() { ); } + #[test] + fn boxes() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, deref_mut, slice +use core::ops::{Deref, DerefMut}; +use core::{marker::Unsize, ops::CoerceUnsized}; + +#[lang = "owned_box"] +pub struct Box { + inner: *mut T, +} +impl Box { + fn new(t: T) -> Self { + #[rustc_box] + Box::new(t) + } +} + +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &**self + } +} + +impl DerefMut for Box { + fn deref_mut(&mut self) -> &mut T { + &mut **self + } +} + +fn f() { + let x = Box::new(5); + x = Box::new(7); + //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` + let x = Box::new(5); + *x = 7; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + let mut y = Box::new(5); + //^^^^^ 💡 weak: variable does not need to be mutable + *x = *y; + //^^^^^^^ 💡 error: cannot mutate immutable variable `x` + let x = Box::new(5); + let closure = || *x = 2; + //^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + #[test] fn allow_unused_mut_for_identifiers_starting_with_underline() { check_diagnostics( From 55b4549602d2f1105aedd7a1357a62db4f128167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Sun, 4 Jun 2023 20:33:33 +0800 Subject: [PATCH 669/806] Show note for type ascription interpreted as a constant pattern, not a new variable Given the code ```rust pub fn main() { const y: i32 = 4; let y: i32 = 3; } ``` `y` in the let binding is actually interpreted as a constant pattern and is not a new variable, causing confusing diagnostics about refutable patterns in local binding. This commit extends the note for type ascription as a constant pattern to `AscribeUserType` patterns as well. --- .../src/thir/pattern/check_match.rs | 7 ++++- tests/ui/mir/issue-112269.rs | 9 ++++++ tests/ui/mir/issue-112269.stderr | 31 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/ui/mir/issue-112269.rs create mode 100644 tests/ui/mir/issue-112269.stderr diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 1e51cb9aa96e7..e7e88351bbd1e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -443,7 +443,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let mut let_suggestion = None; let mut misc_suggestion = None; let mut interpreted_as_const = None; - if let PatKind::Constant { .. } = pat.kind + + if let PatKind::Constant { .. } + | PatKind::AscribeUserType { + subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, + .. + } = pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { // If the pattern to match is an integer literal: diff --git a/tests/ui/mir/issue-112269.rs b/tests/ui/mir/issue-112269.rs new file mode 100644 index 0000000000000..8b9b16647e4d6 --- /dev/null +++ b/tests/ui/mir/issue-112269.rs @@ -0,0 +1,9 @@ +pub fn main() { + const x: i32 = 4; + let x: i32 = 3; + //~^ ERROR refutable pattern in local binding + + const y: i32 = 3; + let y = 4; + //~^ ERROR refutable pattern in local binding +} diff --git a/tests/ui/mir/issue-112269.stderr b/tests/ui/mir/issue-112269.stderr new file mode 100644 index 0000000000000..f5b796027979c --- /dev/null +++ b/tests/ui/mir/issue-112269.stderr @@ -0,0 +1,31 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/issue-112269.rs:3:9 + | +LL | let x: i32 = 3; + | ^ + | | + | patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered + | missing patterns are not covered because `x` is interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `x_var` + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` + +error[E0005]: refutable pattern in local binding + --> $DIR/issue-112269.rs:7:9 + | +LL | let y = 4; + | ^ + | | + | patterns `i32::MIN..=2_i32` and `4_i32..=i32::MAX` not covered + | missing patterns are not covered because `y` is interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `y_var` + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0005`. From 1f5361b40c4ccfa2f806cc9f293d8399a5651b9a Mon Sep 17 00:00:00 2001 From: Victor Gil Date: Sun, 4 Jun 2023 15:49:04 +0200 Subject: [PATCH 670/806] Added custom risc32-imac for esp-espidf target --- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/riscv32imac_esp_espidf.rs | 31 +++++++++++++++++++ src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/esp-idf.md | 9 +++--- 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 05cb7e87a9365..99e4ba24afb1c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1284,6 +1284,7 @@ supported_targets! { ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), + ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), diff --git a/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs new file mode 100644 index 0000000000000..0795065409ad9 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs @@ -0,0 +1,31 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + linker: Some("riscv32-esp-elf-gcc".into()), + cpu: "generic-rv32".into(), + + // As RiscV32IMAC architecture does natively support atomics, + // automatically enable the support for the Rust STD library. + max_atomic_width: Some(64), + atomic_cas: true, + + features: "+m,+a,+c".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3b2463aa5b2dd..d0d300010cbdc 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -298,6 +298,7 @@ target | std | host | notes `riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 8a4ca347e22f5..4bbe35709b087 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -13,11 +13,12 @@ Targets for the [ESP-IDF](https://github.com/espressif/esp-idf) development fram The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies the target processor architecture. The following targets are currently defined: -| Target name | Target CPU(s) | -|--------------------------------|-----------------------| -| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | +| Target name | Target CPU(s) | Minimum ESP-IDF version | +|--------------------------------|-----------------------|-------------------------| +| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.3` | +| `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | -The minimum supported ESP-IDF version is `v4.3`, though it is recommended to use the latest stable release if possible. +It is recommended to use the latest ESP-IDF stable release if possible. ## Building the target From 100db9946b69ddebb7925f0a57ee5a944b239030 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 Jun 2023 15:50:42 +0200 Subject: [PATCH 671/806] Update browser-ui-test version --- .../docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 3ccca1e8b89bc..413b31282f7c9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.5 \ No newline at end of file +0.16.6 \ No newline at end of file From d67e00eb2660614354f8016f6311cdc17ec7ae44 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 Jun 2023 15:55:30 +0200 Subject: [PATCH 672/806] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/sidebar-mobile.goml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index 3b022c7e9d040..4b8337ace3a2f 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -73,16 +73,16 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "color": "rgb(197, 197, 197)", - "background": "rgb(20, 25, 31)", + "color": "#c5c5c5", + "background": "#14191f", }) call-function: ("check-colors", { "theme": "dark", - "color": "rgb(221, 221, 221)", - "background": "rgb(80, 80, 80)", + "color": "#ddd", + "background": "#505050", }) call-function: ("check-colors", { "theme": "light", - "color": "rgb(0, 0, 0)", - "background": "rgb(245, 245, 245)", + "color": "black", + "background": "#F5F5F5", }) From dd2bd03d0a9c3950d19ecc8e907c5b150e27e0ce Mon Sep 17 00:00:00 2001 From: Grisha Vartanyan Date: Sun, 4 Jun 2023 08:06:25 +0200 Subject: [PATCH 673/806] Remove ExtendWith and ExtendElement --- library/alloc/src/vec/mod.rs | 30 ++++++------------------- library/alloc/src/vec/spec_from_elem.rs | 6 ++--- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 47661a3d38429..d89cdff8e366c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2355,7 +2355,7 @@ impl Vec { let len = self.len(); if new_len > len { - self.extend_with(new_len - len, ExtendElement(value)) + self.extend_with(new_len - len, value) } else { self.truncate(new_len); } @@ -2469,26 +2469,10 @@ impl Vec<[T; N], A> { } } -// This code generalizes `extend_with_{element,default}`. -trait ExtendWith { - fn next(&mut self) -> T; - fn last(self) -> T; -} - -struct ExtendElement(T); -impl ExtendWith for ExtendElement { - fn next(&mut self) -> T { - self.0.clone() - } - fn last(self) -> T { - self.0 - } -} - -impl Vec { +impl Vec { #[cfg(not(no_global_oom_handling))] - /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, mut value: E) { + /// Extend the vector by `n` clones of value. + fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); unsafe { @@ -2500,15 +2484,15 @@ impl Vec { // Write all elements except the last one for _ in 1..n { - ptr::write(ptr, value.next()); + ptr::write(ptr, value.clone()); ptr = ptr.add(1); - // Increment the length in every step in case next() panics + // Increment the length in every step in case clone() panics local_len.increment_len(1); } if n > 0 { // We can write the last element directly without cloning needlessly - ptr::write(ptr, value.last()); + ptr::write(ptr, value); local_len.increment_len(1); } diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index ff364c033ee98..da43d17bf3624 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -3,7 +3,7 @@ use core::ptr; use crate::alloc::Allocator; use crate::raw_vec::RawVec; -use super::{ExtendElement, IsZero, Vec}; +use super::{IsZero, Vec}; // Specialization trait used for Vec::from_elem pub(super) trait SpecFromElem: Sized { @@ -13,7 +13,7 @@ pub(super) trait SpecFromElem: Sized { impl SpecFromElem for T { default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } @@ -25,7 +25,7 @@ impl SpecFromElem for T { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } From f9a81e476992b8c1a7e9b67cf8a943a0ff03b978 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 4 Jun 2023 09:13:54 -0500 Subject: [PATCH 674/806] Don't require the output from libtest to be valid UTF-8 On Windows this is sometimes not the case, for reasons I can't track down. This works around the problem, although I'm not sure how to confirm we're not generating invalid build metrics in this case. --- src/bootstrap/render_tests.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/render_tests.rs b/src/bootstrap/render_tests.rs index 872b75f6c1599..06ab820953d08 100644 --- a/src/bootstrap/render_tests.rs +++ b/src/bootstrap/render_tests.rs @@ -88,10 +88,10 @@ impl<'a> Renderer<'a> { } fn render_all(mut self) { - let mut line = String::new(); + let mut line = Vec::new(); loop { line.clear(); - match self.stdout.read_line(&mut line) { + match self.stdout.read_until(b'\n', &mut line) { Ok(_) => {} Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break, Err(err) => panic!("failed to read output of test runner: {err}"), @@ -100,12 +100,13 @@ impl<'a> Renderer<'a> { break; } - match serde_json::from_str(&line) { + match serde_json::from_slice(&line) { Ok(parsed) => self.render_message(parsed), Err(_err) => { // Handle non-JSON output, for example when --nocapture is passed. - print!("{line}"); - let _ = std::io::stdout().flush(); + let mut stdout = std::io::stdout(); + stdout.write_all(&line).unwrap(); + let _ = stdout.flush(); } } } From 9e5573a0d275c71dce59b715d981c6880d30703a Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Tue, 4 Apr 2023 17:55:20 -0700 Subject: [PATCH 675/806] Use 128 bits for TypeId hash - Switch TypeId to 128 bits - Hack around the fact that tracing-subscriber dislikes how TypeId is hashed - Remove lowering of type_id128 from rustc_codegen_llvm - Remove unnecessary `type_id128` intrinsic (just change return type of `type_id`) - Only hash the lower 64 bits of the TypeId - Reword comment --- .../src/interpret/intrinsics.rs | 4 +-- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 +- .../rustc_middle/src/mir/interpret/value.rs | 9 ++++++ compiler/rustc_middle/src/ty/util.rs | 4 +-- library/core/src/any.rs | 31 +++++++++++++++++-- library/core/src/intrinsics.rs | 17 ++++++++++ 6 files changed, 59 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fffb9a7f26487..7192bbc00d556 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -77,7 +77,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( } sym::type_id => { ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_u64(tcx.type_id_hash(tp_ty).as_u64()) + ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) } sym::variant_count => match tp_ty.kind() { // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. @@ -169,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ty = match intrinsic_name { sym::pref_align_of | sym::variant_count => self.tcx.types.usize, sym::needs_drop => self.tcx.types.bool, - sym::type_id => self.tcx.types.u64, + sym::type_id => self.tcx.types.u128, sym::type_name => self.tcx.mk_static_str(), _ => bug!(), }; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 1f18017f00b9d..36c468e778986 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -217,7 +217,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::needs_drop => (1, Vec::new(), tcx.types.bool), sym::type_name => (1, Vec::new(), tcx.mk_static_str()), - sym::type_id => (1, Vec::new(), tcx.types.u64), + sym::type_id => (1, Vec::new(), tcx.types.u128), sym::offset => (2, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 91caf9db336cf..0416411dfe14a 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -97,6 +97,10 @@ impl<'tcx> ConstValue<'tcx> { ConstValue::Scalar(Scalar::from_u64(i)) } + pub fn from_u128(i: u128) -> Self { + ConstValue::Scalar(Scalar::from_u128(i)) + } + pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self { ConstValue::Scalar(Scalar::from_target_usize(i, cx)) } @@ -240,6 +244,11 @@ impl Scalar { Scalar::Int(i.into()) } + #[inline] + pub fn from_u128(i: u128) -> Self { + Scalar::Int(i.into()) + } + #[inline] pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self { Self::from_uint(i, cx.data_layout().pointer_size) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index dce2f5545f51d..c6a99f17eadd6 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -11,7 +11,7 @@ use crate::ty::{ use crate::ty::{GenericArgKind, SubstsRef}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -129,7 +129,7 @@ impl IntTypeExt for IntegerType { impl<'tcx> TyCtxt<'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. - pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash64 { + pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash128 { // We want the type_id be independent of the types free regions, so we // erase them. The erase_regions() call will also anonymize bound // regions, which is desirable too. diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7969f4055dd2b..89c613aef9971 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -153,6 +153,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; +use crate::hash; use crate::intrinsics; /////////////////////////////////////////////////////////////////////////////// @@ -662,10 +663,10 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! -#[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - t: u64, + t: u128, } #[stable(feature = "rust1", since = "1.0.0")] @@ -696,7 +697,31 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - TypeId { t: intrinsics::type_id::() } + #[cfg(bootstrap)] + let t = intrinsics::type_id::() as u128; + #[cfg(not(bootstrap))] + let t: u128 = intrinsics::type_id::(); + TypeId { t } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for TypeId { + #[inline] + fn hash(&self, state: &mut H) { + // We only hash the lower 64 bits of our (128 bit) internal numeric ID, + // because: + // - The hashing algorithm which backs `TypeId` is expected to be + // unbiased and high quality, meaning further mixing would be somewhat + // redundant compared to choosing (the lower) 64 bits arbitrarially. + // - `Hasher::finish` returns a u64 anyway, so the extra entropy we'd + // get from hashing the full value would probably not be useful + // (especially given the previous point about the lower 64 bits being + // high quality on their own). + // - It is correct to do so -- only hashing a subset of `self` is still + // with an `Eq` implementation that considers the entire value, as + // ours does. + (self.t as u64).hash(state); } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 6dca1fe1e6961..9b8612485ac1e 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1057,8 +1057,25 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] #[rustc_safe_intrinsic] #[rustc_nounwind] + #[cfg(bootstrap)] pub fn type_id() -> u64; + /// Gets an identifier which is globally unique to the specified type. This + /// function will return the same value for a type regardless of whichever + /// crate it is invoked in. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. + #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn type_id() -> u128; + /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. /// From b4907a531f596b54ed6fe66d0f208b76179f5e06 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 4 Jun 2023 20:59:27 +0330 Subject: [PATCH 676/806] Fix missing terminator for slice pattern --- .../hir-ty/src/mir/lower/pattern_matching.rs | 94 ++++++++++--------- .../src/handlers/mutability_errors.rs | 21 +++++ 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 5cd1be6842439..ff43c64a9e60f 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -206,56 +206,60 @@ impl MirLowerCtx<'_> { (current, current_else) } Pat::Slice { prefix, slice, suffix } => { - if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) { - let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = - self.temp(TyBuilder::usize(), current, pattern.into())?.into(); - self.push_assignment( - current, - place_len.clone(), - Rvalue::Len((&mut cond_place).clone()), - pattern.into(), - ); - let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); - let next = self.new_basic_block(); - if slice.is_none() { - self.set_terminator( - current, - TerminatorKind::SwitchInt { - discr: Operand::Copy(place_len), - targets: SwitchTargets::static_if( - pattern_len as u128, - next, - else_target, - ), - }, - pattern.into(), - ); - } else { - let c = Operand::from_concrete_const( - pattern_len.to_le_bytes().to_vec(), - MemoryMap::default(), - TyBuilder::usize(), - ); - let discr: Place = - self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + if mode == MatchingMode::Check { + // emit runtime length check for slice + if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) { + let pattern_len = prefix.len() + suffix.len(); + let place_len: Place = + self.temp(TyBuilder::usize(), current, pattern.into())?.into(); self.push_assignment( current, - discr.clone(), - Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)), - pattern.into(), - ); - let discr = Operand::Copy(discr); - self.set_terminator( - current, - TerminatorKind::SwitchInt { - discr, - targets: SwitchTargets::static_if(1, next, else_target), - }, + place_len.clone(), + Rvalue::Len((&mut cond_place).clone()), pattern.into(), ); + let else_target = + *current_else.get_or_insert_with(|| self.new_basic_block()); + let next = self.new_basic_block(); + if slice.is_none() { + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(place_len), + targets: SwitchTargets::static_if( + pattern_len as u128, + next, + else_target, + ), + }, + pattern.into(), + ); + } else { + let c = Operand::from_concrete_const( + pattern_len.to_le_bytes().to_vec(), + MemoryMap::default(), + TyBuilder::usize(), + ); + let discr: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, else_target), + }, + pattern.into(), + ); + } + current = next; } - current = next; } for (i, &pat) in prefix.iter().enumerate() { let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 8795afc2d9739..f61460e317f78 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -993,6 +993,27 @@ fn f() { ); } + #[test] + fn slice_pattern() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, deref_mut, slice, copy +fn x(t: &[u8]) { + match t { + &[a, mut b] | &[a, _, mut b] => { + //^^^^^ 💡 weak: variable does not need to be mutable + + a = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + + } + _ => {} + } +} + "#, + ); + } + #[test] fn boxes() { check_diagnostics( From 70e1dc99672235ae34b6b3981bb0805f97c20179 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 4 Jun 2023 14:54:28 -0700 Subject: [PATCH 677/806] Avoid unwind across `extern "C"` in `thread_local::fast_local.rs` --- .../src/sys/common/thread_local/fast_local.rs | 22 +++++++++---------- .../std/src/sys/common/thread_local/mod.rs | 21 ++++++++++++++++++ library/std/src/thread/mod.rs | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index 447044a798ba6..bc5da1a189677 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -33,20 +33,21 @@ pub macro thread_local_inner { // 1 == dtor registered, dtor not run // 2 == dtor registered and is running or has run #[thread_local] - static mut STATE: $crate::primitive::u8 = 0; + static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); + // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires + // all that comes with it. 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); - } + $crate::thread::local_impl::abort_on_dtor_unwind(|| { + let old_state = STATE.replace(2); + $crate::debug_assert_eq!(old_state, 1); + // Safety: safety requirement is passed on to caller. + unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } + }); } unsafe { - match STATE { + match STATE.get() { // 0 == we haven't registered a destructor, so do // so now. 0 => { @@ -54,7 +55,7 @@ pub macro thread_local_inner { $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, destroy, ); - STATE = 1; + STATE.set(1); $crate::option::Option::Some(&VAL) } // 1 == the destructor is registered and the value @@ -148,7 +149,6 @@ impl fmt::Debug for Key { f.debug_struct("Key").finish_non_exhaustive() } } - impl Key { pub const fn new() -> Key { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 77f645883102c..975509bd412b0 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -101,3 +101,24 @@ mod lazy { } } } + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f712c872708ac..d9973185bc45e 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -206,7 +206,7 @@ cfg_if::cfg_if! { #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { - pub use crate::sys::common::thread_local::{thread_local_inner, Key}; + pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind}; } } } From 1835d0333be220fc23d8b290298838dff5549cad Mon Sep 17 00:00:00 2001 From: Matthew Esposito Date: Sun, 4 Jun 2023 18:06:34 -0400 Subject: [PATCH 678/806] Make RustAnalyzer check off by default --- src/bootstrap/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index f5a93854bf2c4..1a0f0047812ed 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -347,7 +347,7 @@ pub struct RustAnalyzer { impl Step for RustAnalyzer { type Output = (); const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; + const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rust-analyzer") From 7ed7e208abd9f807b96226eb47545f81bc4c9156 Mon Sep 17 00:00:00 2001 From: Matthew Esposito Date: Sun, 4 Jun 2023 18:06:52 -0400 Subject: [PATCH 679/806] Run Rustfmt before RustAnalyzer --- src/bootstrap/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 848fb9eade917..916823edc1437 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -703,8 +703,8 @@ impl<'a> Builder<'a> { check::CargoMiri, check::MiroptTestTools, check::Rls, - check::RustAnalyzer, check::Rustfmt, + check::RustAnalyzer, check::Bootstrap ), Kind::Test => describe!( From 0a1cd5baa4d0ad4994f4038988a76770a6e2150c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 2 Jun 2023 11:07:38 +1000 Subject: [PATCH 680/806] Remove some unnecessary `&mut`s. --- compiler/rustc_monomorphize/src/partitioning.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 79fcd62bc6206..e99551410e022 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -136,7 +136,7 @@ struct PlacedRootMonoItems<'tcx> { // The output CGUs are sorted by name. fn partition<'tcx, I>( tcx: TyCtxt<'tcx>, - mono_items: &mut I, + mono_items: I, max_cgu_count: usize, usage_map: &UsageMap<'tcx>, ) -> Vec> @@ -239,7 +239,7 @@ where fn place_root_mono_items<'tcx, I>( cx: &PartitioningCx<'_, 'tcx>, - mono_items: &mut I, + mono_items: I, ) -> PlacedRootMonoItems<'tcx> where I: Iterator>, @@ -951,12 +951,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { sync::join( || { - let mut codegen_units = partition( - tcx, - &mut items.iter().copied(), - tcx.sess.codegen_units(), - &usage_map, - ); + let mut codegen_units = + partition(tcx, items.iter().copied(), tcx.sess.codegen_units(), &usage_map); codegen_units[0].make_primary(); &*tcx.arena.alloc_from_iter(codegen_units) }, From 17412bae30db7f8f72218387925b8931f42eb08a Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Fri, 7 Apr 2023 02:26:08 -0400 Subject: [PATCH 681/806] Removed use of iteration through a HashMap/HashSet in rustc_incremental and replaced with IndexMap/IndexSet --- compiler/rustc_codegen_llvm/src/lib.rs | 4 +-- compiler/rustc_codegen_ssa/src/back/write.rs | 8 +++--- .../rustc_codegen_ssa/src/traits/backend.rs | 4 +-- .../rustc_incremental/src/assert_dep_graph.rs | 28 +++++++++---------- .../src/assert_module_sources.rs | 4 +-- compiler/rustc_incremental/src/lib.rs | 1 - .../src/persist/dirty_clean.rs | 4 +-- compiler/rustc_incremental/src/persist/fs.rs | 14 +++++----- .../rustc_incremental/src/persist/load.rs | 6 ++-- .../rustc_incremental/src/persist/save.rs | 8 +++--- .../src/persist/work_product.rs | 4 +-- .../rustc_query_system/src/dep_graph/graph.rs | 10 +++---- 12 files changed, 47 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index ff9909c720e54..24968e00cc8e5 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -34,7 +34,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, FatalError, Handler, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_metadata::EncodedMetadata; @@ -356,7 +356,7 @@ impl CodegenBackend for LlvmCodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let (codegen_results, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 10e9e5588f6cc..701d0d73ad38c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -9,7 +9,7 @@ use crate::{ }; use jobserver::{Acquired, Client}; use rustc_ast::attr; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::profiling::TimingGuard; @@ -498,8 +498,8 @@ pub fn start_async_codegen( fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( sess: &Session, compiled_modules: &CompiledModules, -) -> FxHashMap { - let mut work_products = FxHashMap::default(); +) -> FxIndexMap { + let mut work_products = FxIndexMap::default(); if sess.opts.incremental.is_none() { return work_products; @@ -1885,7 +1885,7 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap) { + pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap) { let _timer = sess.timer("finish_ongoing_codegen"); self.shared_emitter_main.check(sess, true); diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index d83bfc74082f6..b3c9ecf8b938b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -6,7 +6,7 @@ use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; @@ -101,7 +101,7 @@ pub trait CodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed>; + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed>; /// This is called on the returned `Box` from `join_codegen` /// diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 22bd12f2e6361..52a84b204d00b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -35,7 +35,7 @@ use crate::errors; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING}; use rustc_graphviz as dot; use rustc_hir as hir; @@ -258,7 +258,7 @@ fn dump_graph(query: &DepGraphQuery) { } #[allow(missing_docs)] -pub struct GraphvizDepGraph(FxHashSet, Vec<(DepKind, DepKind)>); +pub struct GraphvizDepGraph(FxIndexSet, Vec<(DepKind, DepKind)>); impl<'a> dot::GraphWalk<'a> for GraphvizDepGraph { type Node = DepKind; @@ -303,7 +303,7 @@ impl<'a> dot::Labeller<'a> for GraphvizDepGraph { fn node_set<'q>( query: &'q DepGraphQuery, filter: &DepNodeFilter, -) -> Option> { +) -> Option> { debug!("node_set(filter={:?})", filter); if filter.accepts_all() { @@ -315,9 +315,9 @@ fn node_set<'q>( fn filter_nodes<'q>( query: &'q DepGraphQuery, - sources: &Option>, - targets: &Option>, -) -> FxHashSet { + sources: &Option>, + targets: &Option>, +) -> FxIndexSet { if let Some(sources) = sources { if let Some(targets) = targets { walk_between(query, sources, targets) @@ -333,10 +333,10 @@ fn filter_nodes<'q>( fn walk_nodes<'q>( query: &'q DepGraphQuery, - starts: &FxHashSet<&'q DepNode>, + starts: &FxIndexSet<&'q DepNode>, direction: Direction, -) -> FxHashSet { - let mut set = FxHashSet::default(); +) -> FxIndexSet { + let mut set = FxIndexSet::default(); for &start in starts { debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING); if set.insert(start.kind) { @@ -357,9 +357,9 @@ fn walk_nodes<'q>( fn walk_between<'q>( query: &'q DepGraphQuery, - sources: &FxHashSet<&'q DepNode>, - targets: &FxHashSet<&'q DepNode>, -) -> FxHashSet { + sources: &FxIndexSet<&'q DepNode>, + targets: &FxIndexSet<&'q DepNode>, +) -> FxIndexSet { // This is a bit tricky. We want to include a node only if it is: // (a) reachable from a source and (b) will reach a target. And we // have to be careful about cycles etc. Luckily efficiency is not @@ -426,8 +426,8 @@ fn walk_between<'q>( } } -fn filter_edges(query: &DepGraphQuery, nodes: &FxHashSet) -> Vec<(DepKind, DepKind)> { - let uniq: FxHashSet<_> = query +fn filter_edges(query: &DepGraphQuery, nodes: &FxIndexSet) -> Vec<(DepKind, DepKind)> { + let uniq: FxIndexSet<_> = query .edges() .into_iter() .map(|(s, t)| (s.kind, t.kind)) diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index c550e553bb032..82c787605d7d3 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -24,7 +24,7 @@ use crate::errors; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; @@ -52,7 +52,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) { struct AssertModuleSource<'tcx> { tcx: TyCtxt<'tcx>, - available_cgus: FxHashSet, + available_cgus: FxIndexSet, } impl<'tcx> AssertModuleSource<'tcx> { diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 11710c368cefe..b9171fad55ba3 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -4,7 +4,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(never_type)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 43274091cb873..848b81b9d3496 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -21,7 +21,7 @@ use crate::errors; use rustc_ast::{self as ast, Attribute, NestedMetaItem}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit; use rustc_hir::Node as HirNode; @@ -125,7 +125,7 @@ const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; // // type_of for these. -type Labels = FxHashSet; +type Labels = FxIndexSet; /// Represents the requested configuration by rustc_clean/dirty struct Assertion { diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index e3c688b3e98cb..463481f16f371 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -104,7 +104,7 @@ //! implemented. use crate::errors; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; @@ -635,8 +635,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // First do a pass over the crate directory, collecting lock files and // session directories - let mut session_directories = FxHashSet::default(); - let mut lock_files = FxHashSet::default(); + let mut session_directories = FxIndexSet::default(); + let mut lock_files = FxIndexSet::default(); for dir_entry in crate_directory.read_dir()? { let Ok(dir_entry) = dir_entry else { @@ -659,7 +659,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Now map from lock files to session directories - let lock_file_to_session_dir: FxHashMap> = lock_files + let lock_file_to_session_dir: FxIndexMap> = lock_files .into_iter() .map(|lock_file_name| { assert!(lock_file_name.ends_with(LOCK_FILE_EXT)); @@ -705,7 +705,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Filter out `None` directories - let lock_file_to_session_dir: FxHashMap = lock_file_to_session_dir + let lock_file_to_session_dir: FxIndexMap = lock_file_to_session_dir .into_iter() .filter_map(|(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n))) .collect(); @@ -846,7 +846,7 @@ fn delete_old(sess: &Session, path: &Path) { fn all_except_most_recent( deletion_candidates: Vec<(SystemTime, PathBuf, Option)>, -) -> FxHashMap> { +) -> FxIndexMap> { let most_recent = deletion_candidates.iter().map(|&(timestamp, ..)| timestamp).max(); if let Some(most_recent) = most_recent { @@ -856,7 +856,7 @@ fn all_except_most_recent( .map(|(_, path, lock)| (path, lock)) .collect() } else { - FxHashMap::default() + FxIndexMap::default() } } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index a4407a93ff3ba..0f7db10912797 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -1,7 +1,7 @@ //! Code to save/load the dep-graph from files. use crate::errors; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::memmap::Mmap; use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::query::on_disk_cache::OnDiskCache; @@ -16,7 +16,7 @@ use super::file_format; use super::fs::*; use super::work_product; -type WorkProductMap = FxHashMap; +type WorkProductMap = FxIndexMap; #[derive(Debug)] /// Represents the result of an attempt to load incremental compilation data. @@ -147,7 +147,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { let report_incremental_info = sess.opts.unstable_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false); - let mut prev_work_products = FxHashMap::default(); + let mut prev_work_products = FxIndexMap::default(); // If we are only building with -Zquery-dep-graph but without an actual // incr. comp. session directory, we skip this. Otherwise we'd fail diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 7376be6be8b8a..bcffba466413c 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,5 +1,5 @@ use crate::errors; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::join; use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -79,7 +79,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { pub fn save_work_product_index( sess: &Session, dep_graph: &DepGraph, - new_work_products: FxHashMap, + new_work_products: FxIndexMap, ) { if sess.opts.incremental.is_none() { return; @@ -119,7 +119,7 @@ pub fn save_work_product_index( } fn encode_work_product_index( - work_products: &FxHashMap, + work_products: &FxIndexMap, encoder: &mut FileEncoder, ) { let serialized_products: Vec<_> = work_products @@ -146,7 +146,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult pub fn build_dep_graph( sess: &Session, prev_graph: SerializedDepGraph, - prev_work_products: FxHashMap, + prev_work_products: FxIndexMap, ) -> Option { if sess.opts.incremental.is_none() { // No incremental compilation. diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index dc98fbeb0d166..a1dc78596a9ef 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -4,7 +4,7 @@ use crate::errors; use crate::persist::fs::*; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -20,7 +20,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; - let mut saved_files = FxHashMap::default(); + let mut saved_files = FxIndexMap::default(); for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index c0d7386dd6adb..54eaa1d4a7f99 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1,6 +1,6 @@ use parking_lot::Mutex; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -93,7 +93,7 @@ pub struct DepGraphData { /// things available to us. If we find that they are not dirty, we /// load the path to the file storing those work-products here into /// this map. We can later look for and extract that data. - previous_work_products: FxHashMap, + previous_work_products: FxIndexMap, dep_node_debug: Lock, String>>, @@ -116,7 +116,7 @@ impl DepGraph { pub fn new( profiler: &SelfProfilerRef, prev_graph: SerializedDepGraph, - prev_work_products: FxHashMap, + prev_work_products: FxIndexMap, encoder: FileEncoder, record_graph: bool, record_stats: bool, @@ -688,7 +688,7 @@ impl DepGraph { /// Access the map of work-products created during the cached run. Only /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> &FxHashMap { + pub fn previous_work_products(&self) -> &FxIndexMap { &self.data.as_ref().unwrap().previous_work_products } @@ -1048,7 +1048,7 @@ pub struct WorkProduct { /// /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to /// the object file's path, and "dwo" to the dwarf object file's path. - pub saved_files: FxHashMap, + pub saved_files: FxIndexMap, } // Index type for `DepNodeData`'s edges. From 204e2bf5a4828b044a78019a118e12d09bde772d Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Fri, 7 Apr 2023 15:56:33 -0400 Subject: [PATCH 682/806] Updated cranelift codegen to reflect modified trait signature --- compiler/rustc_codegen_cranelift/src/driver/aot.rs | 4 ++-- compiler/rustc_codegen_cranelift/src/lib.rs | 4 ++-- compiler/rustc_codegen_gcc/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index aad9a9647f8c9..d143bcc96ef93 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -54,8 +54,8 @@ impl OngoingCodegen { self, sess: &Session, backend_config: &BackendConfig, - ) -> (CodegenResults, FxHashMap) { - let mut work_products = FxHashMap::default(); + ) -> (CodegenResults, FxIndexMap) { + let mut work_products = FxIndexMap::default(); let mut modules = vec![]; for module_codegen in self.modules { diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 9966cc2ef3c12..095fbe62c1902 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -88,7 +88,7 @@ mod prelude { }; pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT}; - pub(crate) use rustc_data_structures::fx::FxHashMap; + pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; pub(crate) use rustc_index::Idx; @@ -223,7 +223,7 @@ impl CodegenBackend for CraneliftCodegenBackend { ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { Ok(ongoing_codegen .downcast::() .unwrap() diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 442ce0ea54209..ea013c4428cce 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -75,7 +75,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::target_features::supported_target_features; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_metadata::EncodedMetadata; @@ -137,7 +137,7 @@ impl CodegenBackend for GccCodegenBackend { Box::new(res) } - fn join_codegen(&self, ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + fn join_codegen(&self, ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let (codegen_results, work_products) = ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") From 2a96c6e5171e5aa9e5e70639989e7cdb99dc5efc Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Fri, 7 Apr 2023 17:26:30 -0400 Subject: [PATCH 683/806] Fixed compiler error --- .../hotplug_codegen_backend/the_backend.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 7db100a08a106..8a275751e38de 100644 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -15,7 +15,7 @@ extern crate rustc_target; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenResults, CrateInfo}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -49,11 +49,11 @@ impl CodegenBackend for TheBackend { ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let codegen_results = ongoing_codegen .downcast::() .expect("in join_codegen: ongoing_codegen is not a CodegenResults"); - Ok((*codegen_results, FxHashMap::default())) + Ok((*codegen_results, FxIndexMap::default())) } fn link( From 1be19f710c983258282796a111354528908451c5 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Sun, 7 May 2023 19:52:19 -0400 Subject: [PATCH 684/806] Switched some uses to UnordMap --- .../src/assert_module_sources.rs | 9 +- .../src/persist/dirty_clean.rs | 22 +- compiler/rustc_incremental/src/persist/fs.rs | 206 +++++++++--------- .../rustc_incremental/src/persist/fs/tests.rs | 32 +-- .../rustc_incremental/src/persist/load.rs | 4 +- compiler/rustc_interface/src/queries.rs | 4 +- .../src/dep_graph/dep_node.rs | 9 +- 7 files changed, 150 insertions(+), 136 deletions(-) diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 82c787605d7d3..3e12c9b5016be 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -24,7 +24,7 @@ use crate::errors; use rustc_ast as ast; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; @@ -52,7 +52,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) { struct AssertModuleSource<'tcx> { tcx: TyCtxt<'tcx>, - available_cgus: FxIndexSet, + available_cgus: UnordSet, } impl<'tcx> AssertModuleSource<'tcx> { @@ -118,9 +118,8 @@ impl<'tcx> AssertModuleSource<'tcx> { debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); if !self.available_cgus.contains(&cgu_name) { - let mut cgu_names: Vec<&str> = - self.available_cgus.iter().map(|cgu| cgu.as_str()).collect(); - cgu_names.sort(); + let cgu_names: Vec = + self.available_cgus.items().map(|cgu| cgu.as_str().to_owned()).into_sorted(&()); self.tcx.sess.emit_err(errors::NoModuleNamed { span: attr.span, user_path, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 848b81b9d3496..51bdcf8e92b52 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -21,7 +21,8 @@ use crate::errors; use rustc_ast::{self as ast, Attribute, NestedMetaItem}; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit; use rustc_hir::Node as HirNode; @@ -125,7 +126,7 @@ const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; // // type_of for these. -type Labels = FxIndexSet; +type Labels = UnordSet; /// Represents the requested configuration by rustc_clean/dirty struct Assertion { @@ -197,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let (name, mut auto) = self.auto_labels(item_id, attr); let except = self.except(attr); let loaded_from_disk = self.loaded_from_disk(attr); - for e in except.iter() { + for e in except.to_sorted(&(), false) { if !auto.remove(e) { self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } @@ -376,18 +377,21 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - for label in assertion.clean { + assertion.clean.items().all(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); - } - for label in assertion.dirty { + true + }); + assertion.dirty.items().all(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); - } - for label in assertion.loaded_from_disk { + true + }); + assertion.loaded_from_disk.items().all(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); - } + true + }); } } } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 463481f16f371..b44fb37fd7a93 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -104,8 +104,9 @@ //! implemented. use crate::errors; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; @@ -636,7 +637,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // First do a pass over the crate directory, collecting lock files and // session directories let mut session_directories = FxIndexSet::default(); - let mut lock_files = FxIndexSet::default(); + let mut lock_files = UnordSet::default(); for dir_entry in crate_directory.read_dir()? { let Ok(dir_entry) = dir_entry else { @@ -659,9 +660,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Now map from lock files to session directories - let lock_file_to_session_dir: FxIndexMap> = lock_files - .into_iter() - .map(|lock_file_name| { + let lock_file_to_session_dir: UnordMap> = + UnordMap::from(lock_files.into_items().map(|lock_file_name| { assert!(lock_file_name.ends_with(LOCK_FILE_EXT)); let dir_prefix_end = lock_file_name.len() - LOCK_FILE_EXT.len(); let session_dir = { @@ -669,12 +669,11 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { session_directories.iter().find(|dir_name| dir_name.starts_with(dir_prefix)) }; (lock_file_name, session_dir.map(String::clone)) - }) - .collect(); + })); // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - for (lock_file_name, directory_name) in &lock_file_to_session_dir { + lock_file_to_session_dir.items().all(|(lock_file_name, directory_name)| { if directory_name.is_none() { let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { debug!( @@ -682,7 +681,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { crate_directory.join(&lock_file_name).display() ); // Ignore it - continue; + return true; }; let lock_file_path = crate_directory.join(&**lock_file_name); @@ -702,17 +701,18 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { ); } } - } + true + }); // Filter out `None` directories - let lock_file_to_session_dir: FxIndexMap = lock_file_to_session_dir - .into_iter() - .filter_map(|(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n))) - .collect(); + let lock_file_to_session_dir: UnordMap = + UnordMap::from(lock_file_to_session_dir.into_items().filter_map( + |(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n)), + )); // Delete all session directories that don't have a lock file. for directory_name in session_directories { - if !lock_file_to_session_dir.values().any(|dir| *dir == directory_name) { + if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { let path = crate_directory.join(directory_name); if let Err(err) = safe_remove_dir_all(&path) { sess.emit_warning(errors::InvalidGcFailed { path: &path, err }); @@ -721,103 +721,103 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Now garbage collect the valid session directories. - let mut deletion_candidates = vec![]; - - for (lock_file_name, directory_name) in &lock_file_to_session_dir { - debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); + let deletion_candidates = + lock_file_to_session_dir.items().filter_map(|(lock_file_name, directory_name)| { + debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); - let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else { + let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else { debug!( "found session-dir with malformed timestamp: {}", crate_directory.join(directory_name).display() ); // Ignore it - continue; + return None; }; - if is_finalized(directory_name) { - let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( - &lock_file_path, - false, // don't wait - false, // don't create the lock-file - true, - ) { - // get an exclusive lock - Ok(lock) => { - debug!( - "garbage_collect_session_directories() - \ + if is_finalized(directory_name) { + let lock_file_path = crate_directory.join(lock_file_name); + match flock::Lock::new( + &lock_file_path, + false, // don't wait + false, // don't create the lock-file + true, + ) { + // get an exclusive lock + Ok(lock) => { + debug!( + "garbage_collect_session_directories() - \ successfully acquired lock" - ); - debug!( - "garbage_collect_session_directories() - adding \ + ); + debug!( + "garbage_collect_session_directories() - adding \ deletion candidate: {}", - directory_name - ); - - // Note that we are holding on to the lock - deletion_candidates.push(( - timestamp, - crate_directory.join(directory_name), - Some(lock), - )); - } - Err(_) => { - debug!( - "garbage_collect_session_directories() - \ + directory_name + ); + + // Note that we are holding on to the lock + return Some(( + (timestamp, crate_directory.join(directory_name)), + Some(lock), + )); + } + Err(_) => { + debug!( + "garbage_collect_session_directories() - \ not collecting, still in use" - ); + ); + } } - } - } else if is_old_enough_to_be_collected(timestamp) { - // When cleaning out "-working" session directories, i.e. - // session directories that might still be in use by another - // compiler instance, we only look a directories that are - // at least ten seconds old. This is supposed to reduce the - // chance of deleting a directory in the time window where - // the process has allocated the directory but has not yet - // acquired the file-lock on it. - - // Try to acquire the directory lock. If we can't, it - // means that the owning process is still alive and we - // leave this directory alone. - let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( - &lock_file_path, - false, // don't wait - false, // don't create the lock-file - true, - ) { - // get an exclusive lock - Ok(lock) => { - debug!( - "garbage_collect_session_directories() - \ + } else if is_old_enough_to_be_collected(timestamp) { + // When cleaning out "-working" session directories, i.e. + // session directories that might still be in use by another + // compiler instance, we only look a directories that are + // at least ten seconds old. This is supposed to reduce the + // chance of deleting a directory in the time window where + // the process has allocated the directory but has not yet + // acquired the file-lock on it. + + // Try to acquire the directory lock. If we can't, it + // means that the owning process is still alive and we + // leave this directory alone. + let lock_file_path = crate_directory.join(lock_file_name); + match flock::Lock::new( + &lock_file_path, + false, // don't wait + false, // don't create the lock-file + true, + ) { + // get an exclusive lock + Ok(lock) => { + debug!( + "garbage_collect_session_directories() - \ successfully acquired lock" - ); + ); - delete_old(sess, &crate_directory.join(directory_name)); + delete_old(sess, &crate_directory.join(directory_name)); - // Let's make it explicit that the file lock is released at this point, - // or rather, that we held on to it until here - drop(lock); - } - Err(_) => { - debug!( - "garbage_collect_session_directories() - \ + // Let's make it explicit that the file lock is released at this point, + // or rather, that we held on to it until here + drop(lock); + } + Err(_) => { + debug!( + "garbage_collect_session_directories() - \ not collecting, still in use" - ); + ); + } } - } - } else { - debug!( - "garbage_collect_session_directories() - not finalized, not \ + } else { + debug!( + "garbage_collect_session_directories() - not finalized, not \ old enough" - ); - } - } + ); + } + None + }); + let deletion_candidates = UnordMap::from(deletion_candidates); // Delete all but the most recent of the candidates - for (path, lock) in all_except_most_recent(deletion_candidates) { + all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = safe_remove_dir_all(&path) { @@ -829,7 +829,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Let's make it explicit that the file lock is released at this point, // or rather, that we held on to it until here drop(lock); - } + true + }); Ok(()) } @@ -845,18 +846,19 @@ fn delete_old(sess: &Session, path: &Path) { } fn all_except_most_recent( - deletion_candidates: Vec<(SystemTime, PathBuf, Option)>, -) -> FxIndexMap> { - let most_recent = deletion_candidates.iter().map(|&(timestamp, ..)| timestamp).max(); + deletion_candidates: UnordMap<(SystemTime, PathBuf), Option>, +) -> UnordMap> { + let most_recent = deletion_candidates.items().map(|(&(timestamp, _), _)| timestamp).max(); if let Some(most_recent) = most_recent { - deletion_candidates - .into_iter() - .filter(|&(timestamp, ..)| timestamp != most_recent) - .map(|(_, path, lock)| (path, lock)) - .collect() + UnordMap::from( + deletion_candidates + .into_items() + .filter(|&((timestamp, _), _)| timestamp != most_recent) + .map(|((_, path), lock)| (path, lock)), + ) } else { - FxIndexMap::default() + UnordMap::default() } } diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 184796948b67d..90d8a72de07d5 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -2,26 +2,26 @@ use super::*; #[test] fn test_all_except_most_recent() { + let computed: UnordMap<_, Option> = UnordMap::from_iter([ + ((UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4")), None), + ((UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1")), None), + ((UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5")), None), + ((UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3")), None), + ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None), + ]); + let mut paths = UnordSet::default(); + UnordSet::extend_unord(&mut paths, computed.into_items().map(|((_, path), _)| path)); assert_eq!( - all_except_most_recent(vec![ - (UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4"), None), - (UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1"), None), - (UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5"), None), - (UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3"), None), - (UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2"), None), + UnordSet::from(paths), + UnordSet::from_iter([ + PathBuf::from("1"), + PathBuf::from("2"), + PathBuf::from("3"), + PathBuf::from("4") ]) - .keys() - .cloned() - .collect::>(), - [PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] - .into_iter() - .collect::>() ); - assert_eq!( - all_except_most_recent(vec![]).keys().cloned().collect::>(), - FxHashSet::default() - ); + assert!(all_except_most_recent(UnordMap::default()).is_empty()); } #[test] diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 0f7db10912797..b718bed819b2f 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -1,8 +1,8 @@ //! Code to save/load the dep-graph from files. use crate::errors; -use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::memmap::Mmap; +use rustc_data_structures::unord::UnordMap; use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::opaque::MemDecoder; @@ -16,7 +16,7 @@ use super::file_format; use super::fs::*; use super::work_product; -type WorkProductMap = FxIndexMap; +type WorkProductMap = UnordMap; #[derive(Debug)] /// Represents the result of an attempt to load incremental compilation data. diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index c441a8ffd6f94..6975fbd917a4f 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -5,6 +5,7 @@ use crate::passes; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AppendOnlyIndexVec, Lrc, OnceCell, RwLock, WorkerLocal}; @@ -195,7 +196,8 @@ impl<'tcx> Queries<'tcx> { .and_then(|future| { let (prev_graph, prev_work_products) = sess.time("blocked_on_dep_graph_loading", || future.open().open(sess)); - + let prev_work_products = + FxIndexMap::from_iter(prev_work_products.into_sorted(&(), false)); rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products) }) .unwrap_or_else(DepGraph::new_disabled); diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 9e1ca6ab515d8..e0089f2861547 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -46,7 +46,7 @@ use super::{DepContext, DepKind, FingerprintStyle}; use crate::ich::StableHashingContext; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_hir::definitions::DefPathHash; use std::fmt; use std::hash::Hash; @@ -247,3 +247,10 @@ impl HashStable for WorkProductId { self.hash.hash_stable(hcx, hasher) } } +impl ToStableHashKey for WorkProductId { + type KeyType = Fingerprint; + #[inline] + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + self.hash + } +} From cf7dea571695be5db843519db014785c4a0a2786 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Sun, 7 May 2023 20:15:47 -0400 Subject: [PATCH 685/806] Sorted a FxIndexSet for consistent iteration order --- compiler/rustc_incremental/src/persist/fs.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index b44fb37fd7a93..e11245549404d 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -658,6 +658,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // This is something we don't know, leave it alone } } + session_directories.sort(); // Now map from lock files to session directories let lock_file_to_session_dir: UnordMap> = From 96b577860d4e175ccc4698b9e9a8a822b228fc19 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Sun, 7 May 2023 21:08:47 -0400 Subject: [PATCH 686/806] Fixed failing test + minor cleanup --- compiler/rustc_data_structures/src/unord.rs | 5 +++++ compiler/rustc_incremental/src/persist/dirty_clean.rs | 9 +++------ compiler/rustc_incremental/src/persist/fs.rs | 5 ++--- compiler/rustc_incremental/src/persist/fs/tests.rs | 2 +- compiler/rustc_incremental/src/persist/load.rs | 2 +- compiler/rustc_incremental/src/persist/save.rs | 4 ++-- compiler/rustc_incremental/src/persist/work_product.rs | 8 ++++---- compiler/rustc_query_system/src/dep_graph/graph.rs | 3 ++- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 6c8d541463158..02957b38efe7b 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -62,6 +62,11 @@ impl> UnordItems { UnordItems(self.0.filter_map(f)) } + #[inline] + pub fn for_each ()>(self, f: F) { + self.0.for_each(|x| f(x)); + } + #[inline] pub fn max(self) -> Option where diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 51bdcf8e92b52..b7bdffe5b06ae 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -377,20 +377,17 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - assertion.clean.items().all(|label| { + assertion.clean.items().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); - true }); - assertion.dirty.items().all(|label| { + assertion.dirty.items().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); - true }); - assertion.loaded_from_disk.items().all(|label| { + assertion.loaded_from_disk.items().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); - true }); } } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index e11245549404d..c3f1abadacef2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -674,7 +674,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - lock_file_to_session_dir.items().all(|(lock_file_name, directory_name)| { + lock_file_to_session_dir.items().for_each(|(lock_file_name, directory_name)| { if directory_name.is_none() { let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { debug!( @@ -682,7 +682,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { crate_directory.join(&lock_file_name).display() ); // Ignore it - return true; + return; }; let lock_file_path = crate_directory.join(&**lock_file_name); @@ -702,7 +702,6 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { ); } } - true }); // Filter out `None` directories diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 90d8a72de07d5..4c1cb5725dc9c 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -10,7 +10,7 @@ fn test_all_except_most_recent() { ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None), ]); let mut paths = UnordSet::default(); - UnordSet::extend_unord(&mut paths, computed.into_items().map(|((_, path), _)| path)); + paths.extend_unord(all_except_most_recent(computed).into_items().map(|(path, _)| path)); assert_eq!( UnordSet::from(paths), UnordSet::from_iter([ diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index b718bed819b2f..0727523a22980 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -163,7 +163,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { Decodable::decode(&mut work_product_decoder); for swp in work_products { - let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| { + let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { let exists = in_incr_comp_dir_sess(sess, path).exists(); if !exists && sess.opts.unstable_opts.incremental_info { eprintln!("incremental: could not find file for work product: {path}",); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index bcffba466413c..bfaa52f9c8134 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -105,7 +105,7 @@ pub fn save_work_product_index( if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); debug_assert!( - !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) ); } } @@ -113,7 +113,7 @@ pub fn save_work_product_index( // Check that we did not delete one of the current work-products: debug_assert!({ new_work_products.iter().all(|(_, wp)| { - wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) }) }); } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index a1dc78596a9ef..865f90273bdec 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -4,7 +4,7 @@ use crate::errors; use crate::persist::fs::*; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -20,7 +20,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; - let mut saved_files = FxIndexMap::default(); + let mut saved_files = UnordMap::default(); for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); @@ -46,10 +46,10 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - for (_, path) in &work_product.saved_files { + work_product.saved_files.items().for_each(|(_, path)| { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); } - } + }); } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 54eaa1d4a7f99..c9e80a6d9bc13 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -6,6 +6,7 @@ use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; +use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use smallvec::{smallvec, SmallVec}; @@ -1048,7 +1049,7 @@ pub struct WorkProduct { /// /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to /// the object file's path, and "dwo" to the dwarf object file's path. - pub saved_files: FxIndexMap, + pub saved_files: UnordMap, } // Index type for `DepNodeData`'s edges. From 6f2d3dee1790f8066394d1198fd1864b5fae45a3 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Mon, 8 May 2023 17:26:17 -0400 Subject: [PATCH 687/806] Fixed unord mistake --- compiler/rustc_data_structures/src/unord.rs | 5 --- .../src/persist/dirty_clean.rs | 6 ++-- compiler/rustc_incremental/src/persist/fs.rs | 36 ++++++++++--------- .../src/persist/work_product.rs | 2 +- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 02957b38efe7b..6c8d541463158 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -62,11 +62,6 @@ impl> UnordItems { UnordItems(self.0.filter_map(f)) } - #[inline] - pub fn for_each ()>(self, f: F) { - self.0.for_each(|x| f(x)); - } - #[inline] pub fn max(self) -> Option where diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index b7bdffe5b06ae..6381a05dfdc1c 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -377,15 +377,15 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - assertion.clean.items().for_each(|label| { + assertion.clean.to_sorted(&(), false).iter().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); }); - assertion.dirty.items().for_each(|label| { + assertion.dirty.to_sorted(&(), false).iter().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); }); - assertion.loaded_from_disk.items().for_each(|label| { + assertion.loaded_from_disk.to_sorted(&(), false).iter().for_each(|label| { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); }); diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index c3f1abadacef2..f0e5f07230f7e 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -674,9 +674,10 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - lock_file_to_session_dir.items().for_each(|(lock_file_name, directory_name)| { - if directory_name.is_none() { - let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { + lock_file_to_session_dir.to_sorted(&(), false).iter().for_each( + |(lock_file_name, directory_name)| { + if directory_name.is_none() { + let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { debug!( "found lock-file with malformed timestamp: {}", crate_directory.join(&lock_file_name).display() @@ -685,24 +686,25 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { return; }; - let lock_file_path = crate_directory.join(&**lock_file_name); + let lock_file_path = crate_directory.join(&**lock_file_name); - if is_old_enough_to_be_collected(timestamp) { - debug!( - "garbage_collect_session_directories() - deleting \ + if is_old_enough_to_be_collected(timestamp) { + debug!( + "garbage_collect_session_directories() - deleting \ garbage lock file: {}", - lock_file_path.display() - ); - delete_session_dir_lock_file(sess, &lock_file_path); - } else { - debug!( - "garbage_collect_session_directories() - lock file with \ + lock_file_path.display() + ); + delete_session_dir_lock_file(sess, &lock_file_path); + } else { + debug!( + "garbage_collect_session_directories() - lock file with \ no session dir not old enough to be collected: {}", - lock_file_path.display() - ); + lock_file_path.display() + ); + } } - } - }); + }, + ); // Filter out `None` directories let lock_file_to_session_dir: UnordMap = diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 865f90273bdec..b0d173ab30469 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -46,7 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - work_product.saved_files.items().for_each(|(_, path)| { + work_product.saved_files.to_sorted(&(), false).iter().for_each(|(_, path)| { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); From 5eeeed1aa1d84145250d1b0f9561bee4d4e7cda7 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 5 Apr 2023 08:57:54 -0400 Subject: [PATCH 688/806] Greatly decrease the size of `rustc_driver.so` when debuginfo is enabled - Only add -gz if it's supported - Don't include extra unnecessary debuginfo when only debuginfo-level=1 is set - Compress debuginfo sections to reduce the size of debuginfo on disk. before: 650 MB line tables only: 335 MB compressed only: 216 MB compressed and line tables: 186 MB no debuginfo at all: 130 MB I want to investigate why `-C line-tables-only` is still ~tripling the size of the binary, but this seems like a good improvement in the meantime. I've tested that both valgrind and perf can read the debuginfo: ``` (bash@dev-desktop-us-1.infra.rust-lang.org) ~/rust [08:31:08] ; valgrind $(rustup which rustc --toolchain rust_stage2) --version ==441671== Memcheck, a memory error detector ==441671== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==441671== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==441671== Command: /home/gh-jyn514/.local/lib/rustup/toolchains/rust_stage2/bin/rustc --version ==441671== rustc 1.70.0-dev ==441671== ==441671== HEAP SUMMARY: ==441671== in use at exit: 231,289 bytes in 1,874 blocks ==441671== total heap usage: 2,538 allocs, 664 frees, 486,368 bytes allocated ==441671== ==441671== LEAK SUMMARY: ==441671== definitely lost: 70,656 bytes in 1 blocks ==441671== indirectly lost: 0 bytes in 0 blocks ==441671== possibly lost: 0 bytes in 0 blocks ==441671== still reachable: 160,633 bytes in 1,873 blocks ==441671== suppressed: 0 bytes in 0 blocks ==441671== Rerun with --leak-check=full to see details of leaked memory ==441671== ==441671== For lists of detected and suppressed errors, rerun with: -s ==441671== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ; perf record $(rustup which rustc --toolchain rust_stage2) --version rustc 1.70.0-dev [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.005 MB perf.data (70 samples) ] ; perf report Samples: 70 of event 'cycles:u', Event count (approx.): 21356967 Overhead Command Shared Object Symbol 51.55% rustc ld-linux-aarch64.so.1 [.] _dl_lookup_symbol_x 18.70% rustc ld-linux-aarch64.so.1 [.] _dl_relocate_object 11.95% rustc ld-linux-aarch64.so.1 [.] do_lookup_x 5.55% rustc [unknown] [k] 0xffffa9ad41cfcfdc 2.68% rustc libc.so.6 [.] __GI___strlen_asimd 2.42% rustc librustc_driver-1a385c366c35e81a.so [.] llvm::StringMapImpl::LookupBucketFor 2.16% rustc librustc_driver-1a385c366c35e81a.so [.] _GLOBAL__sub_I_X86InstructionSelector.cpp 1.96% rustc libstd-990fe978dab76ef3.so [.] as core::clone::Clone>::clone 1.60% rustc librustc_driver-1a385c366c35e81a.so [.] llvm::cl::opt >::~opt 1.22% rustc ld-linux-aarch64.so.1 [.] strcmp 0.13% rustc ld-linux-aarch64.so.1 [.] stat64 0.05% rustc ld-linux-aarch64.so.1 [.] __minimal_calloc 0.02% rustc ld-linux-aarch64.so.1 [.] __GI___tunables_init 0.02% rustc ld-linux-aarch64.so.1 [.] _dl_start 0.00% rustc [unknown] [k] 0xffffa9ad41cfd844 0.00% rustc ld-linux-aarch64.so.1 [.] _start ``` --- src/bootstrap/builder.rs | 10 +++++++++- src/bootstrap/cc_detect.rs | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 43c859b00631e..9423f309cb1cc 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1673,7 +1673,15 @@ impl<'a> Builder<'a> { self.config.rust_debuginfo_level_tools } }; - cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + if debuginfo_level == 1 { + // Use less debuginfo than the default to save on disk space. + cargo.env(profile_var("DEBUG"), "line-tables-only"); + } else { + cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + }; + if self.cc[&target].args().iter().any(|arg| arg == "-gz") { + rustflags.arg("-Clink-arg=-gz"); + } cargo.env( profile_var("DEBUG_ASSERTIONS"), if mode == Mode::Std { diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index 65c882fb801e5..db3b69d18c8fc 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -69,6 +69,8 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { .opt_level(2) .warnings(false) .debug(false) + // Compress debuginfo + .flag_if_supported("-gz") .target(&target.triple) .host(&build.build.triple); match build.crt_static(target) { From 1205e6cff3b2df4ed8465aa1f4f4b3f8e230b7a3 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 4 Jun 2023 21:37:09 -0500 Subject: [PATCH 689/806] Use the top-level Kind to determine whether Steps are excluded Previously, this would use the `Kind` passed to `--exclude` (and not do any filtering at all if no kind was passed). That meant that `x test linkchecker --exclude std` would fail - you had to explicitly say `--exclude test::std`. Change bootstrap to use the top-level Kind instead, which does the right thing automatically. Note that this breaks things like `x test --exclude doc::std`, but I'm not sure why you'd ever want to do that. --- src/bootstrap/CHANGELOG.md | 1 + src/bootstrap/builder.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index d6924cf2cfb23..1aba0713850ae 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `x.py fmt` now formats only files modified between the merge-base of HEAD and the last commit in the master branch of the rust-lang repository and the current working directory. To restore old behaviour, use `x.py fmt .`. The check mode is not affected by this change. [#105702](https://github.com/rust-lang/rust/pull/105702) - The `llvm.version-check` config option has been removed. Older versions were never supported. If you still need to support older versions (e.g. you are applying custom patches), patch `check_llvm_version` in bootstrap to change the minimum version. [#108619](https://github.com/rust-lang/rust/pull/108619) - The `rust.ignore-git` option has been renamed to `rust.omit-git-hash`. [#110059](https://github.com/rust-lang/rust/pull/110059) +- `--exclude` no longer accepts a `Kind` as part of a Step; instead it uses the top-level Kind of the subcommand. If this matches how you were already using --exclude (e.g. `x test --exclude test::std`), simply remove the kind: `--exclude std`. If you were using a kind that did not match the top-level subcommand, please open an issue explaining why you wanted this feature. ### Non-breaking changes diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 848fb9eade917..5bdba28158abd 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -307,7 +307,7 @@ impl StepDescription { } fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool { - if builder.config.exclude.iter().any(|e| pathset.has(&e.path, e.kind)) { + if builder.config.exclude.iter().any(|e| pathset.has(&e.path, Some(builder.kind))) { println!("Skipping {:?} because it is excluded", pathset); return true; } From af4e6c19377eca3f862bd2d7483c30831e998886 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 4 Jun 2023 22:11:05 -0500 Subject: [PATCH 690/806] Don't double-print status messages in GHA Before: ``` Building stage0 tool jsondocck (x86_64-unknown-linux-gnu) Building stage0 tool jsondocck (x86_64-unknown-linux-gnu) Downloading crates ... ``` After: ``` Building stage0 tool jsondocck (x86_64-unknown-linux-gnu) Downloading crates ... ``` --- src/bootstrap/lib.rs | 1 - src/tools/build_helper/src/ci.rs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index aa5d1bdd37f99..7ed8d2bfa7f54 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1069,7 +1069,6 @@ impl Build { } fn group(&self, msg: &str) -> Option { - self.info(&msg); match self.config.dry_run { DryRun::SelfCheck => None, DryRun::Disabled | DryRun::UserSelected => Some(gha::group(&msg)), diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs index d2e9c324af8c6..d106e5b339b2f 100644 --- a/src/tools/build_helper/src/ci.rs +++ b/src/tools/build_helper/src/ci.rs @@ -46,6 +46,8 @@ pub mod gha { pub fn group(name: impl std::fmt::Display) -> Group { if std::env::var_os("GITHUB_ACTIONS").is_some() { eprintln!("::group::{name}"); + } else { + eprintln!("{name}") } Group(()) } From c73c5dd35fa8171ba1634bf9111073706ccf993a Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 4 Jun 2023 21:40:05 -0500 Subject: [PATCH 691/806] cleanup now that Kind is no longer used for excludes --- src/bootstrap/builder.rs | 45 +++++++--------------------------- src/bootstrap/builder/tests.rs | 37 ++++++++++++---------------- src/bootstrap/config.rs | 5 ++-- src/bootstrap/test.rs | 2 +- 4 files changed, 27 insertions(+), 62 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 5bdba28158abd..1acf522eeab6b 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -8,7 +8,7 @@ use std::fs::{self, File}; use std::hash::Hash; use std::io::{BufRead, BufReader}; use std::ops::Deref; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; @@ -150,29 +150,6 @@ pub struct TaskPath { pub kind: Option, } -impl TaskPath { - pub fn parse(path: impl Into) -> TaskPath { - let mut kind = None; - let mut path = path.into(); - - let mut components = path.components(); - if let Some(Component::Normal(os_str)) = components.next() { - if let Some(str) = os_str.to_str() { - if let Some((found_kind, found_prefix)) = str.split_once("::") { - if found_kind.is_empty() { - panic!("empty kind in task path {}", path.display()); - } - kind = Kind::parse(found_kind); - assert!(kind.is_some()); - path = Path::new(found_prefix).join(components.as_path()); - } - } - } - - TaskPath { path, kind } - } -} - impl Debug for TaskPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(kind) = &self.kind { @@ -216,7 +193,7 @@ impl PathSet { PathSet::Set(set) } - fn has(&self, needle: &Path, module: Option) -> bool { + fn has(&self, needle: &Path, module: Kind) -> bool { match self { PathSet::Set(set) => set.iter().any(|p| Self::check(p, needle, module)), PathSet::Suite(suite) => Self::check(suite, needle, module), @@ -224,9 +201,9 @@ impl PathSet { } // internal use only - fn check(p: &TaskPath, needle: &Path, module: Option) -> bool { - if let (Some(p_kind), Some(kind)) = (&p.kind, module) { - p.path.ends_with(needle) && *p_kind == kind + fn check(p: &TaskPath, needle: &Path, module: Kind) -> bool { + if let Some(p_kind) = &p.kind { + p.path.ends_with(needle) && *p_kind == module } else { p.path.ends_with(needle) } @@ -238,11 +215,7 @@ impl PathSet { /// This is used for `StepDescription::krate`, which passes all matching crates at once to /// `Step::make_run`, rather than calling it many times with a single crate. /// See `tests.rs` for examples. - fn intersection_removing_matches( - &self, - needles: &mut Vec<&Path>, - module: Option, - ) -> PathSet { + fn intersection_removing_matches(&self, needles: &mut Vec<&Path>, module: Kind) -> PathSet { let mut check = |p| { for (i, n) in needles.iter().enumerate() { let matched = Self::check(p, n, module); @@ -307,7 +280,7 @@ impl StepDescription { } fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool { - if builder.config.exclude.iter().any(|e| pathset.has(&e.path, Some(builder.kind))) { + if builder.config.exclude.iter().any(|e| pathset.has(&e, builder.kind)) { println!("Skipping {:?} because it is excluded", pathset); return true; } @@ -562,7 +535,7 @@ impl<'a> ShouldRun<'a> { ) -> Vec { let mut sets = vec![]; for pathset in &self.paths { - let subset = pathset.intersection_removing_matches(paths, Some(kind)); + let subset = pathset.intersection_removing_matches(paths, kind); if subset != PathSet::empty() { sets.push(subset); } @@ -2130,7 +2103,7 @@ impl<'a> Builder<'a> { let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); for path in &self.paths { - if should_run.paths.iter().any(|s| s.has(path, Some(desc.kind))) + if should_run.paths.iter().any(|s| s.has(path, desc.kind)) && !desc.is_excluded( self, &PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind) }), diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index d76b830b0e530..31dcee58216ae 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -101,23 +101,21 @@ fn test_invalid() { #[test] fn test_intersection() { - let set = PathSet::Set( - ["library/core", "library/alloc", "library/std"].into_iter().map(TaskPath::parse).collect(), - ); + let set = |paths: &[&str]| { + PathSet::Set(paths.into_iter().map(|p| TaskPath { path: p.into(), kind: None }).collect()) + }; + let library_set = set(&["library/core", "library/alloc", "library/std"]); let mut command_paths = vec![Path::new("library/core"), Path::new("library/alloc"), Path::new("library/stdarch")]; - let subset = set.intersection_removing_matches(&mut command_paths, None); - assert_eq!( - subset, - PathSet::Set(["library/core", "library/alloc"].into_iter().map(TaskPath::parse).collect()) - ); + let subset = library_set.intersection_removing_matches(&mut command_paths, Kind::Build); + assert_eq!(subset, set(&["library/core", "library/alloc"]),); assert_eq!(command_paths, vec![Path::new("library/stdarch")]); } #[test] fn test_exclude() { let mut config = configure("test", &["A"], &["A"]); - config.exclude = vec![TaskPath::parse("src/tools/tidy")]; + config.exclude = vec!["src/tools/tidy".into()]; let cache = run_build(&[], config); // Ensure we have really excluded tidy @@ -129,21 +127,16 @@ fn test_exclude() { #[test] fn test_exclude_kind() { - let path = PathBuf::from("src/tools/cargotest"); - let exclude = TaskPath::parse("test::src/tools/cargotest"); - assert_eq!(exclude, TaskPath { kind: Some(Kind::Test), path: path.clone() }); + let path = PathBuf::from("compiler/rustc_data_structures"); let mut config = configure("test", &["A"], &["A"]); - // Ensure our test is valid, and `test::Cargotest` would be run without the exclude. - assert!(run_build(&[path.clone()], config.clone()).contains::()); - // Ensure tests for cargotest are skipped. - config.exclude = vec![exclude.clone()]; - assert!(!run_build(&[path.clone()], config).contains::()); - - // Ensure builds for cargotest are not skipped. - let mut config = configure("build", &["A"], &["A"]); - config.exclude = vec![exclude]; - assert!(run_build(&[path], config).contains::()); + // Ensure our test is valid, and `test::Rustc` would be run without the exclude. + assert!(run_build(&[], config.clone()).contains::()); + // Ensure tests for rustc are skipped. + config.exclude = vec![path.clone()]; + assert!(!run_build(&[], config.clone()).contains::()); + // Ensure builds for rustc are not skipped. + assert!(run_build(&[], config).contains::()); } /// Ensure that if someone passes both a single crate and `library`, all library crates get built. diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 8ea7e83637596..42cd7922be6b5 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -16,7 +16,6 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; -use crate::builder::TaskPath; use crate::cache::{Interned, INTERNER}; use crate::cc_detect::{ndk_compiler, Language}; use crate::channel::{self, GitInfo}; @@ -79,7 +78,7 @@ pub struct Config { pub sanitizers: bool, pub profiler: bool, pub omit_git_hash: bool, - pub exclude: Vec, + pub exclude: Vec, pub include_default_paths: bool, pub rustc_error_format: Option, pub json_output: bool, @@ -958,7 +957,7 @@ impl Config { // Set flags. config.paths = std::mem::take(&mut flags.paths); - config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect(); + config.exclude = flags.exclude; config.include_default_paths = flags.include_default_paths; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 29e48481f0f84..31a2a33ebc259 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1529,7 +1529,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the for exclude in &builder.config.exclude { cmd.arg("--skip"); - cmd.arg(&exclude.path); + cmd.arg(&exclude); } // Get paths from cmd args From f5f638c12434e3c277fde8f4245273c2cc3c8110 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Mon, 5 Jun 2023 00:16:20 -0400 Subject: [PATCH 692/806] Fixed to_sorted => to_sorted_stable_ord --- .../src/stable_hasher.rs | 11 ++++ .../src/assert_module_sources.rs | 4 +- .../src/persist/dirty_clean.rs | 16 +++--- compiler/rustc_incremental/src/persist/fs.rs | 55 ++++++++++--------- .../rustc_incremental/src/persist/load.rs | 2 +- .../src/persist/work_product.rs | 9 ++- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 6d57d81c56a4b..a895e28c822cb 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -339,6 +339,8 @@ impl, T2: HashStable, CTX> HashStable for (T1, T2) } } +unsafe impl StableOrd for (T1, T2) {} + impl HashStable for (T1, T2, T3) where T1: HashStable, @@ -353,6 +355,8 @@ where } } +unsafe impl StableOrd for (T1, T2, T3) {} + impl HashStable for (T1, T2, T3, T4) where T1: HashStable, @@ -369,6 +373,11 @@ where } } +unsafe impl StableOrd + for (T1, T2, T3, T4) +{ +} + impl, CTX> HashStable for [T] { default fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); @@ -459,6 +468,8 @@ impl HashStable for str { } } +unsafe impl StableOrd for &str {} + impl HashStable for String { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 3e12c9b5016be..3d9b1c2e7411f 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -118,8 +118,8 @@ impl<'tcx> AssertModuleSource<'tcx> { debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); if !self.available_cgus.contains(&cgu_name) { - let cgu_names: Vec = - self.available_cgus.items().map(|cgu| cgu.as_str().to_owned()).into_sorted(&()); + let cgu_names: Vec<&str> = + self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(true); self.tcx.sess.emit_err(errors::NoModuleNamed { span: attr.span, user_path, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 6381a05dfdc1c..786a0e0d3b230 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -198,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let (name, mut auto) = self.auto_labels(item_id, attr); let except = self.except(attr); let loaded_from_disk = self.loaded_from_disk(attr); - for e in except.to_sorted(&(), false) { + for e in except.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { if !auto.remove(e) { self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } @@ -377,18 +377,20 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - assertion.clean.to_sorted(&(), false).iter().for_each(|label| { + for label in assertion.clean.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); - }); - assertion.dirty.to_sorted(&(), false).iter().for_each(|label| { + } + for label in assertion.dirty.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); - }); - assertion.loaded_from_disk.to_sorted(&(), false).iter().for_each(|label| { + } + for label in + assertion.loaded_from_disk.items().map(|x| x.as_str()).into_sorted_stable_ord(false) + { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); - }); + } } } } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index f0e5f07230f7e..550772a688121 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -661,8 +661,9 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { session_directories.sort(); // Now map from lock files to session directories - let lock_file_to_session_dir: UnordMap> = - UnordMap::from(lock_files.into_items().map(|lock_file_name| { + let lock_file_to_session_dir: UnordMap> = lock_files + .into_items() + .map(|lock_file_name| { assert!(lock_file_name.ends_with(LOCK_FILE_EXT)); let dir_prefix_end = lock_file_name.len() - LOCK_FILE_EXT.len(); let session_dir = { @@ -670,41 +671,45 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { session_directories.iter().find(|dir_name| dir_name.starts_with(dir_prefix)) }; (lock_file_name, session_dir.map(String::clone)) - })); + }) + .into(); // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - lock_file_to_session_dir.to_sorted(&(), false).iter().for_each( - |(lock_file_name, directory_name)| { - if directory_name.is_none() { - let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { + let lock_file_to_session_dir_iter = lock_file_to_session_dir + .items() + .map(|(file, dir)| (file.as_str(), dir.as_ref().map(|y| y.as_str()))); + for (lock_file_name, directory_name) in + lock_file_to_session_dir_iter.into_sorted_stable_ord(false) + { + if directory_name.is_none() { + let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { debug!( "found lock-file with malformed timestamp: {}", crate_directory.join(&lock_file_name).display() ); // Ignore it - return; + continue; }; - let lock_file_path = crate_directory.join(&**lock_file_name); + let lock_file_path = crate_directory.join(&*lock_file_name); - if is_old_enough_to_be_collected(timestamp) { - debug!( - "garbage_collect_session_directories() - deleting \ - garbage lock file: {}", - lock_file_path.display() - ); - delete_session_dir_lock_file(sess, &lock_file_path); - } else { - debug!( - "garbage_collect_session_directories() - lock file with \ - no session dir not old enough to be collected: {}", - lock_file_path.display() - ); - } + if is_old_enough_to_be_collected(timestamp) { + debug!( + "garbage_collect_session_directories() - deleting \ + garbage lock file: {}", + lock_file_path.display() + ); + delete_session_dir_lock_file(sess, &lock_file_path); + } else { + debug!( + "garbage_collect_session_directories() - lock file with \ + no session dir not old enough to be collected: {}", + lock_file_path.display() + ); } - }, - ); + } + } // Filter out `None` directories let lock_file_to_session_dir: UnordMap = diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 0727523a22980..bb479b5bdccda 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -147,7 +147,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { let report_incremental_info = sess.opts.unstable_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false); - let mut prev_work_products = FxIndexMap::default(); + let mut prev_work_products = UnordMap::default(); // If we are only building with -Zquery-dep-graph but without an actual // incr. comp. session directory, we skip this. Otherwise we'd fail diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index b0d173ab30469..ae604b2ca0be7 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -46,10 +46,15 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - work_product.saved_files.to_sorted(&(), false).iter().for_each(|(_, path)| { + for path in work_product + .saved_files + .items() + .map(|(_, path)| path.as_str()) + .into_sorted_stable_ord(false) + { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); } - }); + } } From e31661cb9e5effdae3be7eec55143e2e8ece9e14 Mon Sep 17 00:00:00 2001 From: Esme Yi Date: Mon, 5 Jun 2023 15:15:09 +0800 Subject: [PATCH 693/806] Address @bjorn3's comments. --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 9f0ca3863bb60..4975a8778ddde 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -377,6 +377,7 @@ pub fn create_wrapper_file( SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 }; } BinaryFormat::Xcoff => { + // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss. file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 }; @@ -389,7 +390,7 @@ pub fn create_wrapper_file( value: offset + 4, size: 0, kind: SymbolKind::Unknown, - scope: SymbolScope::Dynamic, + scope: SymbolScope::Compilation, weak: false, section: SymbolSection::Section(section), flags: SymbolFlags::Xcoff { @@ -488,6 +489,7 @@ pub fn create_compressed_metadata_file_for_xcoff( symbol_name: &str, ) -> Vec { assert!(file.format() == BinaryFormat::Xcoff); + // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss. file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug); From 5531d46c95f4a2745458790a640abc0ec198fd78 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Mon, 5 Jun 2023 10:55:47 +0330 Subject: [PATCH 694/806] Emit `'_` for lifetime generics in `HirDisplay` --- crates/hir-ty/src/display.rs | 41 ++++++++++--------- crates/hir-ty/src/tests/patterns.rs | 2 +- crates/hir-ty/src/tests/regression.rs | 4 +- .../src/handlers/extract_function.rs | 24 +++++++++++ 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index d3dba52d5f07c..f90e025c7cc62 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1223,7 +1223,8 @@ fn hir_fmt_generics( generic_def: Option, ) -> Result<(), HirDisplayError> { let db = f.db; - if parameters.len(Interner) > 0 { + let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); + if parameters.len(Interner) + lifetime_args_count > 0 { let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { match generic_def .map(|generic_def_id| db.generic_defaults(generic_def_id)) @@ -1268,26 +1269,28 @@ fn hir_fmt_generics( } else { parameters.as_slice(Interner) }; - if !parameters_to_write.is_empty() { + if !parameters_to_write.is_empty() || lifetime_args_count != 0 { write!(f, "<")?; - - if f.display_target.is_source_code() { - let mut first = true; - for generic_arg in parameters_to_write { - if !first { - write!(f, ", ")?; - } - first = false; - - if generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error) - { - write!(f, "_")?; - } else { - generic_arg.hir_fmt(f)?; - } + let mut first = true; + for _ in 0..lifetime_args_count { + if !first { + write!(f, ", ")?; + } + first = false; + write!(f, "'_")?; + } + for generic_arg in parameters_to_write { + if !first { + write!(f, ", ")?; + } + first = false; + if f.display_target.is_source_code() + && generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error) + { + write!(f, "_")?; + } else { + generic_arg.hir_fmt(f)?; } - } else { - f.write_joined(parameters_to_write, ", ")?; } write!(f, ">")?; diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index d683113d5d65e..0f5a3e1752cf5 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl + //^^^ VaListImpl<'_> "#, ); } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index a7b0f46e5e54a..f18c953a7afd2 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -896,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard + 150..152 '{}': MutexGuard<'_, T> 234..238 'self': &{unknown} 240..290 '{ ...()); }': () 250..251 'w': &Mutex 276..287 '*(w.lock())': BufWriter 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard + 278..286 'w.lock()': MutexGuard<'_, BufWriter> "#]], ); } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 7587aea55cfa3..2a67909e63719 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -5478,6 +5478,30 @@ fn $0fun_name(i: T) { ); } + #[test] + fn dont_emit_type_with_hidden_lifetime_parameter() { + // FIXME: We should emit a `` generic argument for the generated function + check_assist( + extract_function, + r#" +struct Struct<'a, T>(&'a T); +fn func(i: Struct<'_, T>) { + $0foo(i);$0 +} +"#, + r#" +struct Struct<'a, T>(&'a T); +fn func(i: Struct<'_, T>) { + fun_name(i); +} + +fn $0fun_name(i: Struct<'_, T>) { + foo(i); +} +"#, + ); + } + #[test] fn preserve_generics_from_body() { check_assist( From 896ccb96063526818aa560af43d3a918df97fe48 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:26:53 +0000 Subject: [PATCH 695/806] Properly compare types for `Option::as_deref` suggestion --- .../src/traits/error_reporting/suggestions.rs | 2 +- .../suggest-option-asderef-unfixable.rs | 6 ---- .../suggest-option-asderef-unfixable.stderr | 33 ++++--------------- .../suggest-option-asderef.fixed | 9 +++++ .../suggest-option-asderef.rs | 9 +++++ .../suggest-option-asderef.stderr | 33 ++++++++++++++++--- 6 files changed, 54 insertions(+), 38 deletions(-) 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 42038dbc3d82e..4ca83904f4754 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3593,7 +3593,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let Some(deref_target_did) = tcx.lang_items().deref_target() && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) && let Ok(deref_target) = tcx.try_normalize_erasing_regions(param_env, projection) - && deref_target == target_ty + && infcx.can_eq(param_env, deref_target, target_ty) { let help = if let hir::Mutability::Mut = needs_mut && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index cc9ba5514fef1..ac0831ce65508 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -10,10 +10,6 @@ fn no_args() -> Option<()> { Some(()) } -fn generic_ref(_: &T) -> Option<()> { - Some(()) -} - extern "C" fn takes_str_but_wrong_abi(_: &str) -> Option<()> { Some(()) } @@ -33,8 +29,6 @@ fn main() { //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments - let _ = produces_string().and_then(generic_ref); - //~^ ERROR type mismatch in function arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 079909eb48d1d..ecfbd27b180e6 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -1,5 +1,5 @@ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:28:40 + --> $DIR/suggest-option-asderef-unfixable.rs:24:40 | LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { | ------------------------------------------------------ found signature defined here @@ -15,7 +15,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` - --> $DIR/suggest-option-asderef-unfixable.rs:30:40 + --> $DIR/suggest-option-asderef-unfixable.rs:26:40 | LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); | -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` @@ -27,7 +27,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` - --> $DIR/suggest-option-asderef-unfixable.rs:32:40 + --> $DIR/suggest-option-asderef-unfixable.rs:28:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); | -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -40,7 +40,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0593]: function is expected to take 1 argument, but it takes 0 arguments - --> $DIR/suggest-option-asderef-unfixable.rs:34:40 + --> $DIR/suggest-option-asderef-unfixable.rs:30:40 | LL | fn no_args() -> Option<()> { | -------------------------- takes 0 arguments @@ -54,28 +54,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:36:40 - | -LL | fn generic_ref(_: &T) -> Option<()> { - | -------------------------------------- found signature defined here -... -LL | let _ = produces_string().and_then(generic_ref); - | -------- ^^^^^^^^^^^ expected due to this - | | - | required by a bound introduced by this call - | - = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a _) -> _` -note: required by a bound in `Option::::and_then` - --> $SRC_DIR/core/src/option.rs:LL:COL -help: do not borrow the argument - | -LL - fn generic_ref(_: &T) -> Option<()> { -LL + fn generic_ref(_: T) -> Option<()> { - | - -error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:38:45 + --> $DIR/suggest-option-asderef-unfixable.rs:32:45 | LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { | ------------------------------------------------------ found signature defined here @@ -90,7 +69,7 @@ LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0593, E0631. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef.fixed b/tests/ui/mismatched_types/suggest-option-asderef.fixed index 08805999341ff..5c42ece3c5d09 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.fixed +++ b/tests/ui/mismatched_types/suggest-option-asderef.fixed @@ -16,6 +16,11 @@ fn generic(_: T) -> Option<()> { Some(()) } +fn generic_ref(_: T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + fn main() { let _: Option<()> = produces_string().as_deref().and_then(takes_str); //~^ ERROR type mismatch in function arguments @@ -27,4 +32,8 @@ fn main() { //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref_mut()` first let _ = produces_string().and_then(generic); + + let _ = produces_string().as_deref().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first } diff --git a/tests/ui/mismatched_types/suggest-option-asderef.rs b/tests/ui/mismatched_types/suggest-option-asderef.rs index 3cfb2ffa828c6..a5278b8fb1618 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef.rs @@ -16,6 +16,11 @@ fn generic(_: T) -> Option<()> { Some(()) } +fn generic_ref(_: &T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + fn main() { let _: Option<()> = produces_string().and_then(takes_str); //~^ ERROR type mismatch in function arguments @@ -27,4 +32,8 @@ fn main() { //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref_mut()` first let _ = produces_string().and_then(generic); + + let _ = produces_string().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first } diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index 46da19d2bf4f2..01341603dde3f 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -1,5 +1,5 @@ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:20:52 + --> $DIR/suggest-option-asderef.rs:25:52 | LL | fn takes_str(_: &str) -> Option<()> { | ----------------------------------- found signature defined here @@ -19,7 +19,7 @@ LL | let _: Option<()> = produces_string().as_deref().and_then(takes_str); | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:23:55 + --> $DIR/suggest-option-asderef.rs:28:55 | LL | fn takes_str(_: &str) -> Option<()> { | ----------------------------------- found signature defined here @@ -39,7 +39,7 @@ LL | let _: Option> = produces_string().as_deref().map(takes_str) | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:26:55 + --> $DIR/suggest-option-asderef.rs:31:55 | LL | fn takes_str_mut(_: &mut str) -> Option<()> { | ------------------------------------------- found signature defined here @@ -58,6 +58,31 @@ help: call `Option::as_deref_mut()` first LL | let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); | +++++++++++++++ -error: aborting due to 3 previous errors +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:36:40 + | +LL | fn generic_ref(_: &T) -> Option<()> { + | -------------------------------------- found signature defined here +... +LL | let _ = produces_string().and_then(generic_ref); + | -------- ^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a _) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn generic_ref(_: &T) -> Option<()> { +LL + fn generic_ref(_: T) -> Option<()> { + | +help: call `Option::as_deref()` first + | +LL | let _ = produces_string().as_deref().and_then(generic_ref); + | +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0631`. From c12575d3173eb6ba985482808a87e54f23ad328e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:34:06 +0000 Subject: [PATCH 696/806] Normalize in infcx instead of globally for `Option::as_deref` suggestion The projection may contain inference variables. These inference variables are local to the local inference context. Using `tcx.normalize_erasing_regions` doesn't work here because this method is global and does not have access to the inference context. It's therefore unable to deal with the inference variables. We normalize in the local inference context instead, which knowns about the inference variables. --- .../src/traits/error_reporting/suggestions.rs | 3 ++- .../suggest-option-asderef-inference-var.rs | 9 +++++++ ...uggest-option-asderef-inference-var.stderr | 24 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr 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 4ca83904f4754..80d0faca670a7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3592,7 +3592,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Extract `::Target` assoc type and check that it is `T` && let Some(deref_target_did) = tcx.lang_items().deref_target() && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) - && let Ok(deref_target) = tcx.try_normalize_erasing_regions(param_env, projection) + && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection) + && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) && infcx.can_eq(param_env, deref_target, target_ty) { let help = if let hir::Mutability::Mut = needs_mut diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs new file mode 100644 index 0000000000000..5febbbe392b24 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs @@ -0,0 +1,9 @@ +fn deref_int(a: &i32) -> i32 { + *a +} + +fn main() { + // https://github.com/rust-lang/rust/issues/112293 + let _has_inference_vars: Option = Some(0).map(deref_int); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr new file mode 100644 index 0000000000000..71c4729e31038 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -0,0 +1,24 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-inference-var.rs:7:56 + | +LL | fn deref_int(a: &i32) -> i32 { + | ---------------------------- found signature defined here +... +LL | let _has_inference_vars: Option = Some(0).map(deref_int); + | --- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn({integer}) -> _` + found function signature `for<'a> fn(&'a i32) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn deref_int(a: &i32) -> i32 { +LL + fn deref_int(a: i32) -> i32 { + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`. From 20c6223b6fcef8b90277c1305367b15f50bfd604 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 5 Jun 2023 19:11:20 +0800 Subject: [PATCH 697/806] ci: Upgrade loongarch64-linux-gnu GCC to 13.1.0 --- src/ci/docker/README.md | 2 +- src/ci/docker/scripts/crosstool-ng-git.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index e799d7c968893..852f2e2093595 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -270,7 +270,7 @@ For targets: `loongarch64-unknown-linux-gnu` - Operating System > Linux kernel version = 5.19.16 - Binary utilities > Version of binutils = 2.40 - C-library > glibc version = 2.36 -- C compiler > gcc version = 12.2.0 +- C compiler > gcc version = 13.1.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `mips-linux-gnu.defconfig` diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh index 449cc476f9ab5..b8d3991532717 100644 --- a/src/ci/docker/scripts/crosstool-ng-git.sh +++ b/src/ci/docker/scripts/crosstool-ng-git.sh @@ -2,7 +2,7 @@ set -ex URL=https://github.com/crosstool-ng/crosstool-ng -REV=943364711a650d9b9e84c1b42c91cc0265b6ab5c +REV=227d99d7f3115f3a078595a580d2b307dcd23e93 mkdir crosstool-ng cd crosstool-ng From 768a6c5931e4674a5a80185c130199dc86fa2970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 5 Jun 2023 13:05:30 +0300 Subject: [PATCH 698/806] Add back sysroot-abi feature gate to rust-analyzer --- Cargo.lock | 1 + crates/rust-analyzer/Cargo.toml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 322a67383b030..e36aef6a6aa8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,6 +1505,7 @@ dependencies = [ "parking_lot 0.12.1", "parking_lot_core 0.9.6", "proc-macro-api", + "proc-macro-srv-cli", "profile", "project-model", "rayon", diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 97bd920920601..77f02a83101e3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -68,6 +68,7 @@ ide-db.workspace = true ide-ssr.workspace = true ide.workspace = true proc-macro-api.workspace = true +proc-macro-srv-cli.workspace = true profile.workspace = true project-model.workspace = true stdx.workspace = true @@ -94,4 +95,5 @@ mbe.workspace = true [features] jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] -in-rust-tree = ["ide/in-rust-tree", "syntax/in-rust-tree"] +sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"] +in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"] From edf342afc6dc8358a0dcb8aeea4b4fa071864977 Mon Sep 17 00:00:00 2001 From: kadiwa Date: Mon, 5 Jun 2023 13:52:47 +0200 Subject: [PATCH 699/806] bootstrap: remove dep `is-terminal` --- src/bootstrap/Cargo.lock | 13 ------------- src/bootstrap/Cargo.toml | 1 - src/bootstrap/config.rs | 3 +-- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 8f8778efee796..d48866d58ce1b 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -51,7 +51,6 @@ dependencies = [ "filetime", "hex", "ignore", - "is-terminal", "junction", "libc", "object", @@ -386,18 +385,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "is-terminal" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys", -] - [[package]] name = "itoa" version = "1.0.2" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 367c6190967c6..85eb543e48ead 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -30,7 +30,6 @@ path = "bin/sccache-plus-cl.rs" test = false [dependencies] -is-terminal = "0.4" build_helper = { path = "../tools/build_helper" } cmake = "0.1.38" filetime = "0.2" diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 8ea7e83637596..2bc8441759f4d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -12,6 +12,7 @@ use std::collections::{HashMap, HashSet}; use std::env; use std::fmt; use std::fs; +use std::io::IsTerminal; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; @@ -894,8 +895,6 @@ define_config! { impl Config { pub fn default_opts() -> Config { - use is_terminal::IsTerminal; - let mut config = Config::default(); config.llvm_optimize = true; config.ninja_in_file = true; From c5145dc87a2b750a12e8f36d84ff6fb4f8edfdf4 Mon Sep 17 00:00:00 2001 From: Simon Farre Date: Thu, 25 May 2023 22:22:00 +0200 Subject: [PATCH 700/806] Fix #111961 r=Mark-Simulacrum Makes the Python pretty printer library source'able from within GDB after spawn. This makes `rust-gdb` not the required approach. It also provides the possibility for GUI:s that wrap GDB, to debug Rust using the pretty printer library; as well as other projects such as for instance Pernosco, which previously was not possible. This won't introduce any new unexpected behaviors for users of `rust-gdb` --- src/etc/gdb_load_rust_pretty_printers.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/etc/gdb_load_rust_pretty_printers.py b/src/etc/gdb_load_rust_pretty_printers.py index 856b5df2de70b..491b6ba9e9e69 100644 --- a/src/etc/gdb_load_rust_pretty_printers.py +++ b/src/etc/gdb_load_rust_pretty_printers.py @@ -1,3 +1,14 @@ +# Add this folder to the python sys path; GDB Python-interpreter will now find modules in this path +import sys +from os import path +self_dir = path.dirname(path.realpath(__file__)) +sys.path.append(self_dir) + import gdb import gdb_lookup -gdb_lookup.register_printers(gdb.current_objfile()) + +# current_objfile can be none; even with `gdb foo-app`; sourcing this file after gdb init now works +try: + gdb_lookup.register_printers(gdb.current_objfile()) +except Exception: + gdb_lookup.register_printers(gdb.selected_inferior().progspace) From 62c8c7cca8f4e8133878c6e42ebb3c4236b3e7ff Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Mon, 1 May 2023 13:08:26 +0200 Subject: [PATCH 701/806] Correct LVI print function test --- .../run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks index 8a5493650a720..e02fe09488916 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks @@ -1,6 +1,6 @@ CHECK: print CHECK: lfence -CHECK: lfence -CHECK: lfence -CHECK: callq 0x{{[[:xdigit:]]*}} <_Unwind_Resume> -CHECK-NEXT: ud2 +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] From 5ff8767b36256650561ecbeac565e9b7e1f34470 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 5 Jun 2023 14:19:09 +0200 Subject: [PATCH 702/806] Update to LLVM 16.0.5 --- .gitmodules | 2 +- src/llvm-project | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4596ae17d0238..6b7160bfe1530 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/16.0-2023-04-05 + branch = rustc/16.0-2023-06-05 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/src/llvm-project b/src/llvm-project index 533d3f338b804..22897bce7bfed 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 533d3f338b804d54e5d0ac4fba6276af23002d9c +Subproject commit 22897bce7bfedc9cd3953a33419b346936263500 From 679f29aa73704c5e5bb9acdd7671fc85571e7393 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 5 Jun 2023 13:25:23 +0100 Subject: [PATCH 703/806] Ignore fluent message reordering in `git blame` --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4c445f635d7d2..19078c1b842ae 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -14,3 +14,5 @@ c34fbfaad38cf5829ef5cfe780dc9d58480adeaa cf2dff2b1e3fa55fa5415d524200070d0d7aacfe # Run rustfmt on bootstrap b39a1d6f1a30ba29f25d7141038b9a5bf0126e36 +# reorder fluent message files +f97fddab91fbf290ea5b691fe355d6f915220b6e From 48c46f275b6431966edf243965b027aaf0f4e09d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 5 Jun 2023 15:31:18 +0200 Subject: [PATCH 704/806] Move write! arguments directly into the string --- src/librustdoc/html/render/mod.rs | 4 ++-- src/librustdoc/html/render/print_item.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index df17ab07b576d..d9b1cfcc11190 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1039,8 +1039,8 @@ fn render_attributes_in_pre<'a, 'b: 'a>( // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) { - for a in it.attributes(tcx, false) { - write!(w, "
{}
", a).unwrap(); + for attr in it.attributes(tcx, false) { + write!(w, "
{attr}
").unwrap(); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5bf5b9ef61809..0ddd1a9ff7b73 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1435,17 +1435,17 @@ fn item_proc_macro( let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(buffer, "{}!() {{ /* proc-macro */ }}", name).unwrap(); + write!(buffer, "{name}!() {{ /* proc-macro */ }}").unwrap(); } MacroKind::Attr => { - write!(buffer, "#[{}]", name).unwrap(); + write!(buffer, "#[{name}]").unwrap(); } MacroKind::Derive => { - write!(buffer, "#[derive({})]", name).unwrap(); + write!(buffer, "#[derive({name})]").unwrap(); if !m.helpers.is_empty() { buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap(); for attr in &m.helpers { - writeln!(buffer, " #[{}]", attr).unwrap(); + writeln!(buffer, " #[{attr}]").unwrap(); } buffer.write_str("}\n").unwrap(); } From b048396f440274d8809baa59bc2bdf3aae8aeaa2 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 5 Jun 2023 04:53:30 +0200 Subject: [PATCH 705/806] Update field-offset and enable unstable_offset_of This enables usage of the offset_of!() macro in the compiler, through the wrappers in memoffset and then in field-offset. --- Cargo.lock | 19 ++++++++++++++----- compiler/rustc_query_impl/Cargo.toml | 4 +++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7aa317ad7501..53a9741e2e39b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -849,7 +849,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.8.0", "scopeguard", ] @@ -1204,11 +1204,11 @@ dependencies = [ [[package]] name = "field-offset" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.0", "rustc_version", ] @@ -2157,6 +2157,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -3988,7 +3997,7 @@ version = "0.0.0" dependencies = [ "field-offset", "measureme", - "memoffset", + "memoffset 0.9.0", "rustc-rayon-core", "rustc_ast", "rustc_data_structures", diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index c9353b6fc9fb9..ac697a3ae3e99 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] -memoffset = { version = "0.8.0", features = ["unstable_const"] } field-offset = "0.3.5" measureme = "10.0.0" rustc_ast = { path = "../rustc_ast" } @@ -25,5 +24,8 @@ rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" tracing = "0.1" +# Not used directly, but included to enable the unstable_offset_of feature +memoffset = { version = "0.9.0", features = ["unstable_offset_of"] } + [features] rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"] From e30c52d428a2e4347244fd382f956ab04c100d6c Mon Sep 17 00:00:00 2001 From: Luca Scherzer <83914554+lucascherzer@users.noreply.github.com> Date: Mon, 5 Jun 2023 16:01:09 +0200 Subject: [PATCH 706/806] fix spelling error --- compiler/rustc_driver/src/lib.rs | 2 +- src/librustdoc/visit_ast.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 4eabba575f42a..0cd0b51b6ad49 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,4 +1,4 @@ -// This crate is intentionally empty and a rexport of `rustc_driver_impl` to allow the code in +// This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. pub use rustc_driver_impl::*; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 1689445b9ef7c..db35355289349 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -147,9 +147,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // `#[macro_export] macro_rules!` items are reexported at the top level of the // crate, regardless of where they're defined. We want to document the - // top level rexport of the macro, not its original definition, since - // the rexport defines the path that a user will actually see. Accordingly, - // we add the rexport as an item here, and then skip over the original + // top level re-export of the macro, not its original definition, since + // the re-export defines the path that a user will actually see. Accordingly, + // we add the re-export as an item here, and then skip over the original // definition in `visit_item()` below. // // We also skip `#[macro_export] macro_rules!` that have already been inserted, From 604ffab063e0b488f43b6f8faf60421a4533dbc0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 5 Jun 2023 14:22:45 +0000 Subject: [PATCH 707/806] Avoid going through queries if a value of type `AssocItem` is already available --- compiler/rustc_hir_analysis/src/astconv/mod.rs | 2 +- compiler/rustc_hir_analysis/src/check/compare_impl_item.rs | 4 ++-- compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- compiler/rustc_trait_selection/src/traits/object_safety.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3d78ea9aa9ba1..9e78d9858a6da 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1601,7 +1601,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.associated_items(pred.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) + .filter(|item| item.opt_rpitit_info.is_none()) .map(|item| item.def_id), ); } 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 31b89525f15d4..d95862420da11 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1216,7 +1216,7 @@ fn compare_number_of_generics<'tcx>( // has mismatched type or const generic arguments, then the method that it's // inheriting the generics from will also have mismatched arguments, and // we'll report an error for that instead. Delay a bug for safety, though. - if tcx.opt_rpitit_info(trait_.def_id).is_some() { + if trait_.opt_rpitit_info.is_some() { return Err(tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, "errors comparing numbers of generics of trait/impl functions were not emitted", @@ -2006,7 +2006,7 @@ pub(super) fn check_type_bounds<'tcx>( // 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() { + let impl_ty_span = if impl_ty.opt_rpitit_info.is_some() { tcx.def_span(impl_ty_def_id) } else { match tcx.hir().get_by_def_id(impl_ty_def_id) { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 3971a4c01d661..c9e74896ac08e 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -188,7 +188,7 @@ fn missing_items_err( full_impl_span: Span, ) { let missing_items = - missing_items.iter().filter(|trait_item| tcx.opt_rpitit_info(trait_item.def_id).is_none()); + missing_items.iter().filter(|trait_item| trait_item.opt_rpitit_info.is_none()); let missing_items_msg = missing_items .clone() diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 048302187cf9f..e61cc19354e0f 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -161,7 +161,7 @@ fn object_safety_violations_for_trait( .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) - .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) + .filter(|item| item.opt_rpitit_info.is_none()) .map(|item| { let ident = item.ident(tcx); ObjectSafetyViolation::GAT(ident.name, ident.span) From 1fa769234e8345a1bcc687d31104b458b2be81d7 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 5 Jun 2023 14:25:00 +0000 Subject: [PATCH 708/806] Ensure space is inserted after keyword in `unused_delims` --- compiler/rustc_lint/src/unused.rs | 59 ++++++++++---- tests/ui/lint/lint-unnecessary-parens.fixed | 8 ++ tests/ui/lint/lint-unnecessary-parens.rs | 8 ++ tests/ui/lint/lint-unnecessary-parens.stderr | 86 +++++++++++++++++--- 4 files changed, 131 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8f75fa11dd9a2..04df23c736b0d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -556,6 +556,7 @@ trait UnusedDelimLint { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ); fn is_expr_delims_necessary( @@ -624,6 +625,7 @@ trait UnusedDelimLint { ctx: UnusedDelimsCtx, left_pos: Option, right_pos: Option, + is_kw: bool, ) { // If `value` has `ExprKind::Err`, unused delim lint can be broken. // For example, the following code caused ICE. @@ -667,7 +669,7 @@ trait UnusedDelimLint { left_pos.is_some_and(|s| s >= value.span.lo()), right_pos.is_some_and(|s| s <= value.span.hi()), ); - self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space); + self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw); } fn emit_unused_delims( @@ -677,6 +679,7 @@ trait UnusedDelimLint { spans: Option<(Span, Span)>, msg: &str, keep_space: (bool, bool), + is_kw: bool, ) { let primary_span = if let Some((lo, hi)) = spans { if hi.is_empty() { @@ -690,7 +693,7 @@ trait UnusedDelimLint { let suggestion = spans.map(|(lo, hi)| { let sm = cx.sess().source_map(); let lo_replace = - if keep_space.0 && + if (keep_space.0 || is_kw) && let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { " " } else { @@ -720,7 +723,7 @@ trait UnusedDelimLint { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { use rustc_ast::ExprKind::*; - let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { + let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind { // Do not lint `unused_braces` in `if let` expressions. If(ref cond, ref block, _) if !matches!(cond.kind, Let(_, _, _)) @@ -728,7 +731,7 @@ trait UnusedDelimLint { { let left = e.span.lo() + rustc_span::BytePos(2); let right = block.span.lo(); - (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true) } // Do not lint `unused_braces` in `while let` expressions. @@ -738,27 +741,27 @@ trait UnusedDelimLint { { let left = e.span.lo() + rustc_span::BytePos(5); let right = block.span.lo(); - (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true) } ForLoop(_, ref cond, ref block, ..) => { - (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo())) + (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()), true) } Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { let left = e.span.lo() + rustc_span::BytePos(5); - (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None) + (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true) } Ret(Some(ref value)) => { let left = e.span.lo() + rustc_span::BytePos(3); - (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) + (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true) } - Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None), + Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), Assign(_, ref value, _) | AssignOp(.., ref value) => { - (value, UnusedDelimsCtx::AssignedValue, false, None, None) + (value, UnusedDelimsCtx::AssignedValue, false, None, None, false) } // either function/method call, or something this lint doesn't care about ref call_or_other => { @@ -778,12 +781,20 @@ trait UnusedDelimLint { return; } for arg in args_to_check { - self.check_unused_delims_expr(cx, arg, ctx, false, None, None); + self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false); } return; } }; - self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos); + self.check_unused_delims_expr( + cx, + &value, + ctx, + followed_by_block, + left_pos, + right_pos, + is_kw, + ); } fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { @@ -794,7 +805,7 @@ trait UnusedDelimLint { None => UnusedDelimsCtx::AssignedValue, Some(_) => UnusedDelimsCtx::AssignedValueLetElse, }; - self.check_unused_delims_expr(cx, init, ctx, false, None, None); + self.check_unused_delims_expr(cx, init, ctx, false, None, None, false); } } StmtKind::Expr(ref expr) => { @@ -805,6 +816,7 @@ trait UnusedDelimLint { false, None, None, + false, ); } _ => {} @@ -824,6 +836,7 @@ trait UnusedDelimLint { false, None, None, + false, ); } } @@ -879,6 +892,7 @@ impl UnusedDelimLint for UnusedParens { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ) { match value.kind { ast::ExprKind::Paren(ref inner) => { @@ -893,7 +907,7 @@ impl UnusedDelimLint for UnusedParens { _, ) if node.lazy())) { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } ast::ExprKind::Let(_, ref expr, _) => { @@ -904,6 +918,7 @@ impl UnusedDelimLint for UnusedParens { followed_by_block, None, None, + false, ); } _ => {} @@ -942,7 +957,7 @@ impl UnusedParens { .span .find_ancestor_inside(value.span) .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); - self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); + self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false); } } } @@ -967,6 +982,7 @@ impl EarlyLintPass for UnusedParens { true, None, None, + true, ); for stmt in &block.stmts { ::check_stmt(self, cx, stmt); @@ -985,6 +1001,7 @@ impl EarlyLintPass for UnusedParens { false, None, None, + true, ); } } @@ -1043,6 +1060,7 @@ impl EarlyLintPass for UnusedParens { false, None, None, + false, ); } ast::TyKind::Paren(r) => { @@ -1057,7 +1075,7 @@ impl EarlyLintPass for UnusedParens { .find_ancestor_inside(ty.span) .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); - self.emit_unused_delims(cx, ty.span, spans, "type", (false, false)); + self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); } } self.with_self_ty_parens = false; @@ -1130,6 +1148,7 @@ impl UnusedDelimLint for UnusedBraces { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ) { match value.kind { ast::ExprKind::Block(ref inner, None) @@ -1170,7 +1189,7 @@ impl UnusedDelimLint for UnusedBraces { && !value.span.from_expansion() && !inner.span.from_expansion() { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } } @@ -1183,6 +1202,7 @@ impl UnusedDelimLint for UnusedBraces { followed_by_block, None, None, + false, ); } _ => {} @@ -1207,6 +1227,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1220,6 +1241,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1233,6 +1255,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1247,6 +1270,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } @@ -1258,6 +1282,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index 9c144324f2f7e..bafac05d8daa7 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -35,6 +35,14 @@ pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { panic!() } +pub fn parens_with_keyword(e: &[()]) -> i32 { + if true {} //~ ERROR unnecessary parentheses around `if` + while true {} //~ ERROR unnecessary parentheses around `while` + for _ in e {} //~ ERROR unnecessary parentheses around `for` + match 1 { _ => ()} //~ ERROR unnecessary parentheses around `match` + return 1; //~ ERROR unnecessary parentheses around `return` value +} + macro_rules! baz { ($($foo:expr),+) => { ($($foo),*) diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 4fd9cabb3b0b2..ce537a4dc1da0 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -35,6 +35,14 @@ pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { panic!() } +pub fn parens_with_keyword(e: &[()]) -> i32 { + if(true) {} //~ ERROR unnecessary parentheses around `if` + while(true) {} //~ ERROR unnecessary parentheses around `while` + for _ in(e) {} //~ ERROR unnecessary parentheses around `for` + match(1) { _ => ()} //~ ERROR unnecessary parentheses around `match` + return(1); //~ ERROR unnecessary parentheses around `return` value +} + macro_rules! baz { ($($foo:expr),+) => { ($($foo),*) diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index e13620f06ce08..2ad07530f8c8d 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -63,8 +63,68 @@ LL - (5) LL + 5 | +error: unnecessary parentheses around `if` condition + --> $DIR/lint-unnecessary-parens.rs:39:7 + | +LL | if(true) {} + | ^ ^ + | +help: remove these parentheses + | +LL - if(true) {} +LL + if true {} + | + +error: unnecessary parentheses around `while` condition + --> $DIR/lint-unnecessary-parens.rs:40:10 + | +LL | while(true) {} + | ^ ^ + | +help: remove these parentheses + | +LL - while(true) {} +LL + while true {} + | + +error: unnecessary parentheses around `for` iterator expression + --> $DIR/lint-unnecessary-parens.rs:41:13 + | +LL | for _ in(e) {} + | ^ ^ + | +help: remove these parentheses + | +LL - for _ in(e) {} +LL + for _ in e {} + | + +error: unnecessary parentheses around `match` scrutinee expression + --> $DIR/lint-unnecessary-parens.rs:42:10 + | +LL | match(1) { _ => ()} + | ^ ^ + | +help: remove these parentheses + | +LL - match(1) { _ => ()} +LL + match 1 { _ => ()} + | + +error: unnecessary parentheses around `return` value + --> $DIR/lint-unnecessary-parens.rs:43:11 + | +LL | return(1); + | ^ ^ + | +help: remove these parentheses + | +LL - return(1); +LL + return 1; + | + error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:44:31 + --> $DIR/lint-unnecessary-parens.rs:52:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -76,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:45:33 + --> $DIR/lint-unnecessary-parens.rs:53:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -88,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:49:9 + --> $DIR/lint-unnecessary-parens.rs:57:9 | LL | bar((true)); | ^ ^ @@ -100,7 +160,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:51:8 + --> $DIR/lint-unnecessary-parens.rs:59:8 | LL | if (true) {} | ^ ^ @@ -112,7 +172,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:52:11 + --> $DIR/lint-unnecessary-parens.rs:60:11 | LL | while (true) {} | ^ ^ @@ -124,7 +184,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:53:11 + --> $DIR/lint-unnecessary-parens.rs:61:11 | LL | match (true) { | ^ ^ @@ -136,7 +196,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:56:16 + --> $DIR/lint-unnecessary-parens.rs:64:16 | LL | if let 1 = (1) {} | ^ ^ @@ -148,7 +208,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:57:19 + --> $DIR/lint-unnecessary-parens.rs:65:19 | LL | while let 1 = (2) {} | ^ ^ @@ -160,7 +220,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:73:24 + --> $DIR/lint-unnecessary-parens.rs:81:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -172,7 +232,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:18 + --> $DIR/lint-unnecessary-parens.rs:83:18 | LL | let mut _a = (0); | ^ ^ @@ -184,7 +244,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:76:10 + --> $DIR/lint-unnecessary-parens.rs:84:10 | LL | _a = (0); | ^ ^ @@ -196,7 +256,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:77:11 + --> $DIR/lint-unnecessary-parens.rs:85:11 | LL | _a += (1); | ^ ^ @@ -207,5 +267,5 @@ LL - _a += (1); LL + _a += 1; | -error: aborting due to 17 previous errors +error: aborting due to 22 previous errors From b35f243c89907ba86b55fac5f86f591f7f342cde Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Mon, 5 Jun 2023 15:10:03 +0200 Subject: [PATCH 709/806] Verify that (almost) all `ret` instructions have been replaced --- tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh index 235bb603b842b..04a34724518e8 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh @@ -33,6 +33,15 @@ function check { ${objdump} --disassemble-symbols="${func}" --demangle \ ${enclave} > ${asm} ${filecheck} --input-file ${asm} ${checks} + + if [ "${func_re}" != "rust_plus_one_global_asm" && + "${func_re}" != "cmake_plus_one_c_global_asm" ]; then + # The assembler cannot avoid explicit `ret` instructions. Sequences + # of `shlq $0x0, (%rsp); lfence; retq` are used instead. + # https://www.intel.com/content/www/us/en/developer/articles/technical/ + # software-security-guidance/technical-documentation/load-value-injection.html + ${filecheck} --implicit-check-not ret --input-file ${asm} ${checks} + fi } build From 9d3482c403f781a0d1c81508a7fa5bda5e56ceee Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 29 May 2023 15:43:15 +0000 Subject: [PATCH 710/806] Better group RFC ui tests together --- .../ui/rfc-2005-default-binding-mode/enum.rs | 22 --------- tests/ui/rfc-2005-default-binding-mode/for.rs | 9 ---- tests/ui/rfc-2005-default-binding-mode/lit.rs | 24 ---------- .../ui/rfc-2005-default-binding-mode/slice.rs | 7 --- .../bind-by-move-no-guards.rs | 0 .../former-E0008-now-pass.rs | 0 .../rfc-basic-examples.rs | 0 .../rfc-reject-double-move-across-arms.rs | 0 .../rfc-reject-double-move-across-arms.stderr | 0 .../rfc-reject-double-move-in-first-arm.rs | 0 ...rfc-reject-double-move-in-first-arm.stderr | 0 .../rfc-1014-2.rs | 0 .../rfc-1014.rs | 0 ...-hide-behind-direct-unsafe-ptr-embedded.rs | 0 ...low-hide-behind-direct-unsafe-ptr-param.rs | 0 ...ide-behind-indirect-unsafe-ptr-embedded.rs | 0 ...w-hide-behind-indirect-unsafe-ptr-param.rs | 0 .../allow-use-behind-cousin-variant.rs | 0 ...cant-hide-behind-direct-struct-embedded.rs | 0 ...-hide-behind-direct-struct-embedded.stderr | 0 .../cant-hide-behind-direct-struct-param.rs | 0 ...ant-hide-behind-direct-struct-param.stderr | 0 ...nt-hide-behind-doubly-indirect-embedded.rs | 0 ...ide-behind-doubly-indirect-embedded.stderr | 0 .../cant-hide-behind-doubly-indirect-param.rs | 0 ...t-hide-behind-doubly-indirect-param.stderr | 0 ...nt-hide-behind-indirect-struct-embedded.rs | 0 ...ide-behind-indirect-struct-embedded.stderr | 0 .../cant-hide-behind-indirect-struct-param.rs | 0 ...t-hide-behind-indirect-struct-param.stderr | 0 .../feature-gate.no_gate.stderr | 0 .../feature-gate.rs | 0 .../feature-gate.with_gate.stderr | 0 .../fn-ptr-is-structurally-matchable.rs | 0 ...-61188-match-slice-forbidden-without-eq.rs | 0 ...88-match-slice-forbidden-without-eq.stderr | 0 ...2307-match-ref-ref-forbidden-without-eq.rs | 0 ...-match-ref-ref-forbidden-without-eq.stderr | 0 .../issue-63479-match-fnptr.rs | 0 .../issue-63479-match-fnptr.stderr | 0 .../issue-6804.rs | 0 .../issue-6804.stderr | 0 ...ty-array-allowed-without-eq-issue-62336.rs | 0 .../match-forbidden-without-eq.rs | 0 .../match-forbidden-without-eq.stderr | 0 ...tch-nonempty-array-forbidden-without-eq.rs | 0 ...nonempty-array-forbidden-without-eq.stderr | 0 .../match-requires-both-partialeq-and-eq.rs | 0 ...atch-requires-both-partialeq-and-eq.stderr | 0 .../phantom-data-is-structurally-matchable.rs | 0 .../rfc1445/eq-allows-match-on-ty-in-macro.rs | 0 .../rfc1445/eq-allows-match.rs | 0 .../rfcs/{ => rfc-1623-static}/rfc1623-2.rs | 0 .../{ => rfc-1623-static}/rfc1623-2.stderr | 0 .../rfcs/{ => rfc-1623-static}/rfc1623-3.rs | 0 .../{ => rfc-1623-static}/rfc1623-3.stderr | 0 .../ui/rfcs/{ => rfc-1623-static}/rfc1623.rs | 0 .../1717-dllimport}/library-override.rs | 0 .../rfc-1717-dllimport/missing-link-attr.rs | 0 .../missing-link-attr.stderr | 0 .../rfc-1717-dllimport/multiple-renames.rs | 0 .../multiple-renames.stderr | 0 .../rfc-1717-dllimport/rename-modifiers.rs | 0 .../rename-modifiers.stderr | 0 .../rfc-1717-dllimport/rename-to-empty.rs | 0 .../rfc-1717-dllimport/rename-to-empty.stderr | 0 .../drop-order.rs} | 0 .../issue-103052-1.rs | 0 .../issue-103052-1.stderr | 0 .../issue-103052-2.current.stderr | 0 .../issue-103052-2.next.stderr | 0 .../issue-103052-2.rs | 0 ...ermination-trait-for-box-dyn-error-err.rs} | 0 ...termination-trait-for-box-dyn-error-ok.rs} | 0 .../termination-trait-for-never.rs | 0 ...mination-trait-for-result-box-error_err.rs | 0 .../termination-trait-for-str-err.rs} | 0 ...str.rs => termination-trait-for-str-ok.rs} | 0 .../termination-trait-impl-trait.rs | 0 .../termination-trait-impl-trait.stderr | 0 .../termination-trait-in-test-should-panic.rs | 0 ...mination-trait-in-test-should-panic.stderr | 0 .../termination-trait-in-test.rs | 0 .../termination-trait-main-i32.rs | 0 .../termination-trait-main-i32.stderr | 0 .../termination-trait-main-wrong-type.rs | 0 .../termination-trait-main-wrong-type.stderr | 0 .../termination-trait-not-satisfied.rs | 0 .../termination-trait-not-satisfied.stderr | 0 .../termination-trait-test-wrong-type.rs | 0 .../termination-trait-test-wrong-type.stderr | 0 .../borrowck-issue-49631.rs | 0 .../borrowck-issue-49631.stderr | 0 .../rfc-2005-default-binding-mode/const.rs | 0 .../const.stderr | 0 .../rfc-2005-default-binding-mode/enum-ok.rs | 45 +++++++++++++++++++ .../rfc-2005-default-binding-mode/enum.rs | 29 ++---------- .../rfc-2005-default-binding-mode/enum.stderr | 0 .../explicit-mut.rs | 0 .../explicit-mut.stderr | 0 .../rfc-2005-default-binding-mode/for-ok.rs | 20 +++++++++ .../rfcs/rfc-2005-default-binding-mode/for.rs | 23 +++------- .../rfc-2005-default-binding-mode/for.stderr | 0 .../issue-44912-or.rs | 0 .../issue-44912-or.stderr | 0 .../rfc-2005-default-binding-mode/lit-ok.rs | 34 ++++++++++++++ .../rfcs/rfc-2005-default-binding-mode/lit.rs | 26 ++++------- .../rfc-2005-default-binding-mode/lit.stderr | 0 .../no-double-error.rs | 0 .../no-double-error.stderr | 0 .../rfc-2005-default-binding-mode/slice-ok.rs | 25 +++++++++++ .../rfc-2005-default-binding-mode/slice.rs | 26 ++--------- .../slice.stderr | 0 .../auxiliary/enums.rs | 0 .../auxiliary/monovariants.rs | 0 .../auxiliary/structs.rs | 0 .../auxiliary/unstable.rs | 0 .../auxiliary/variants.rs | 0 .../borrowck-exhaustive.rs | 0 .../borrowck-non-exhaustive.rs | 0 .../borrowck-non-exhaustive.stderr | 0 .../rfc-2008-non-exhaustive/enum-as-cast.rs | 0 .../enum-as-cast.stderr | 0 .../rfc-2008-non-exhaustive/enum.rs | 0 .../rfc-2008-non-exhaustive/enum.stderr | 0 .../enum_same_crate.rs | 0 .../enum_same_crate_empty_match.rs | 0 .../enum_same_crate_empty_match.stderr | 0 .../improper_ctypes/auxiliary/types.rs | 0 .../improper_ctypes/extern_crate_improper.rs | 0 .../extern_crate_improper.stderr | 0 .../improper_ctypes/same_crate_proper.rs | 0 .../invalid-attribute.rs | 0 .../invalid-attribute.stderr | 0 .../omitted-patterns.rs | 0 .../omitted-patterns.stderr | 0 .../stable-omitted-patterns.rs | 0 .../stable-omitted-patterns.stderr | 0 .../rfc-2008-non-exhaustive/struct.rs | 0 .../rfc-2008-non-exhaustive/struct.stderr | 0 .../structs_same_crate.rs | 0 .../uninhabited/auxiliary/uninhabited.rs | 0 .../uninhabited/coercions.rs | 0 .../uninhabited/coercions.stderr | 0 .../uninhabited/coercions_same_crate.rs | 0 .../uninhabited/coercions_same_crate.stderr | 0 .../uninhabited/indirect_match.rs | 0 .../uninhabited/indirect_match.stderr | 0 .../uninhabited/indirect_match_same_crate.rs | 0 .../indirect_match_same_crate.stderr | 0 ...indirect_match_with_exhaustive_patterns.rs | 0 ...rect_match_with_exhaustive_patterns.stderr | 0 ...tch_with_exhaustive_patterns_same_crate.rs | 0 .../issue-65157-repeated-match-arm.rs | 0 .../issue-65157-repeated-match-arm.stderr | 0 .../uninhabited/match.rs | 0 .../uninhabited/match.stderr | 0 .../uninhabited/match_same_crate.rs | 0 .../uninhabited/match_same_crate.stderr | 0 .../match_with_exhaustive_patterns.rs | 0 .../match_with_exhaustive_patterns.stderr | 0 ...tch_with_exhaustive_patterns_same_crate.rs | 0 .../uninhabited/patterns.rs | 0 .../uninhabited/patterns_same_crate.rs | 0 .../uninhabited/patterns_same_crate.stderr | 0 .../rfc-2008-non-exhaustive/variant.rs | 0 .../rfc-2008-non-exhaustive/variant.stderr | 0 .../variants_fictive_visibility.rs | 0 .../variants_same_crate.rs | 0 .../downcast-unsafe-trait-objects.rs | 0 .../manual-self-impl-for-unsafe-obj.rs | 0 .../static-dispatch-unsafe-object.rs | 0 .../rfc-2091-track-caller/call-chain.rs | 0 .../caller-location-fnptr-rt-ctfe-equiv.rs | 0 ...caller-location-fnptr-rt-ctfe-equiv.stderr | 0 .../caller-location-intrinsic.rs | 0 .../const-caller-location.rs | 0 .../diverging-caller-location.rs | 0 .../rfc-2091-track-caller/error-odd-syntax.rs | 0 .../error-odd-syntax.stderr | 0 .../error-with-invalid-abi.rs | 0 .../error-with-invalid-abi.stderr | 0 .../rfc-2091-track-caller/error-with-main.rs | 0 .../error-with-main.stderr | 0 .../rfc-2091-track-caller/error-with-naked.rs | 0 .../error-with-naked.stderr | 0 .../rfc-2091-track-caller/error-with-start.rs | 0 .../error-with-start.stderr | 0 .../intrinsic-wrapper.rs | 0 .../macro-declaration.rs | 0 .../mir-inlined-macro.rs | 0 .../rfc-2091-track-caller/only-for-fns.rs | 0 .../rfc-2091-track-caller/only-for-fns.stderr | 0 .../{ => rfcs}/rfc-2091-track-caller/pass.rs | 0 .../std-panic-locations.rs | 0 .../track-caller-attribute.rs | 0 .../rfc-2091-track-caller/track-caller-ffi.rs | 0 .../rfc-2091-track-caller/tracked-closure.rs | 0 .../tracked-fn-ptr-with-arg.rs | 0 .../rfc-2091-track-caller/tracked-fn-ptr.rs | 0 .../tracked-trait-impls.rs | 0 .../tracked-trait-obj.rs | 0 .../rfc-2093-infer-outlives/cross-crate.rs | 0 .../cross-crate.stderr | 0 .../dont-infer-static.rs | 0 .../dont-infer-static.stderr | 0 .../rfc-2093-infer-outlives/enum.rs | 0 .../rfc-2093-infer-outlives/enum.stderr | 0 .../rfc-2093-infer-outlives/explicit-dyn.rs | 0 .../explicit-dyn.stderr | 0 .../rfc-2093-infer-outlives/explicit-enum.rs | 0 .../explicit-enum.stderr | 0 .../explicit-projection.rs | 0 .../explicit-projection.stderr | 0 .../explicit-struct.rs | 0 .../explicit-struct.stderr | 0 .../rfc-2093-infer-outlives/explicit-union.rs | 0 .../explicit-union.stderr | 0 .../rfc-2093-infer-outlives/issue-54467.rs | 0 .../rfc-2093-infer-outlives/nested-enum.rs | 0 .../nested-enum.stderr | 0 .../rfc-2093-infer-outlives/nested-regions.rs | 0 .../nested-regions.stderr | 0 .../rfc-2093-infer-outlives/nested-structs.rs | 0 .../nested-structs.stderr | 0 .../rfc-2093-infer-outlives/nested-union.rs | 0 .../nested-union.stderr | 0 .../rfc-2093-infer-outlives/privacy.rs | 0 .../rfc-2093-infer-outlives/projection.rs | 0 .../rfc-2093-infer-outlives/projection.stderr | 0 .../rfc-2093-infer-outlives/reference.rs | 0 .../rfc-2093-infer-outlives/reference.stderr | 0 .../regions-enum-not-wf.rs | 0 .../regions-enum-not-wf.stderr | 0 ...egions-outlives-nominal-type-region-rev.rs | 0 ...ns-outlives-nominal-type-region-rev.stderr | 0 .../regions-outlives-nominal-type-region.rs | 0 ...egions-outlives-nominal-type-region.stderr | 0 .../regions-outlives-nominal-type-type-rev.rs | 0 ...ions-outlives-nominal-type-type-rev.stderr | 0 .../regions-outlives-nominal-type-type.rs | 0 .../regions-outlives-nominal-type-type.stderr | 0 .../regions-struct-not-wf.rs | 0 .../regions-struct-not-wf.stderr | 0 .../rfc-2093-infer-outlives/self-dyn.rs | 0 .../rfc-2093-infer-outlives/self-dyn.stderr | 0 .../rfc-2093-infer-outlives/self-structs.rs | 0 .../self-structs.stderr | 0 .../crate-path-non-absolute.rs | 0 .../crate-path-non-absolute.stderr | 0 .../keyword-crate-as-identifier.rs | 0 .../keyword-crate-as-identifier.stderr | 0 .../auxiliary/xcrate.rs | 0 .../non-existent-1.rs | 0 .../non-existent-1.stderr | 0 .../non-existent-2.rs | 0 .../non-existent-2.stderr | 0 .../non-existent-3.rs | 0 .../non-existent-3.stderr | 0 .../not-allowed.rs | 0 .../not-allowed.stderr | 0 .../single-segment.rs | 0 .../single-segment.stderr | 0 .../rfc-2294-if-let-guard/bindings.rs | 0 .../rfc-2294-if-let-guard/bindings.stderr | 0 .../rfc-2294-if-let-guard/feature-gate.rs | 0 .../rfc-2294-if-let-guard/feature-gate.stderr | 0 .../rfc-2294-if-let-guard/run-pass.rs | 0 .../rfc-2294-if-let-guard/typeck.rs | 0 .../rfc-2294-if-let-guard/typeck.stderr | 0 .../{ => rfcs}/rfc-2294-if-let-guard/warns.rs | 0 .../rfc-2294-if-let-guard/warns.stderr | 0 .../rfc-2302-self-struct-ctor.rs | 0 .../convert-id-const-with-gate.rs | 0 .../dbg-macro-expected-behavior.rs | 0 .../dbg-macro-expected-behavior.run.stderr | 0 .../dbg-macro-move-semantics.rs | 0 .../dbg-macro-move-semantics.stderr | 0 .../dbg-macro-requires-debug.rs | 0 .../dbg-macro-requires-debug.stderr | 0 .../feature-gate-do_not_recommend.rs | 0 .../feature-gate-do_not_recommend.stderr | 0 .../incorrect-locations.rs | 0 .../incorrect-locations.stderr | 0 .../unstable-feature.rs | 0 .../unstable-feature.stderr | 0 ...gnof-sizeof-pure-can-be-used-as-idents.rs} | 0 ...mod_file_nonascii_with_path_allowed-aux.rs | 0 .../crate_name_nonascii_forbidden-1.rs | 0 .../crate_name_nonascii_forbidden-1.stderr | 0 .../crate_name_nonascii_forbidden-2.rs | 0 .../crate_name_nonascii_forbidden-2.stderr | 0 .../extern_block_nonascii_forbidden.rs | 0 .../extern_block_nonascii_forbidden.stderr | 0 .../idents-normalized.rs | 0 .../mod_file_nonascii_forbidden.rs | 0 .../mod_file_nonascii_forbidden.stderr | 0 .../mod_file_nonascii_with_path_allowed.rs | 0 .../mod_inline_nonascii_allowed.rs | 0 .../no_mangle_nonascii_forbidden.rs | 0 .../no_mangle_nonascii_forbidden.stderr | 0 .../ast-lowering-does-not-wrap-let-chains.rs | 0 .../ast-pretty-check.rs | 0 .../ast-pretty-check.stdout | 0 .../ast-validate-guards.rs | 0 .../ast-validate-guards.stderr | 0 .../chains-without-let.rs | 0 .../chains-without-let.stderr | 0 .../disallowed-positions.rs | 0 .../disallowed-positions.stderr | 0 ...-else-does-not-interact-with-let-chains.rs | 0 ...e-does-not-interact-with-let-chains.stderr | 0 .../rfc-2497-if-let-chains/feature-gate.rs | 0 .../feature-gate.stderr | 0 .../invalid-let-in-a-valid-let-context.rs | 0 .../invalid-let-in-a-valid-let-context.stderr | 0 .../irrefutable-lets.disallowed.stderr | 0 .../irrefutable-lets.rs | 0 .../rfc-2497-if-let-chains/issue-88498.rs | 0 .../rfc-2497-if-let-chains/issue-90722.rs | 0 .../rfc-2497-if-let-chains/issue-92145.rs | 0 .../rfc-2497-if-let-chains/issue-93150.rs | 0 .../rfc-2497-if-let-chains/issue-93150.stderr | 0 .../rfc-2497-if-let-chains/issue-99938.rs | 0 .../no-double-assigments.rs | 0 .../protect-precedences.rs | 0 .../protect-precedences.stderr | 0 .../then-else-blocks.rs | 0 .../attr-without-param.rs | 0 .../attr-without-param.stderr | 0 .../auxiliary/ident-mac.rs | 0 .../auxiliary/param-attrs.rs | 0 ...-64682-dropping-first-attrs-in-impl-fns.rs | 0 .../rfc-2565-param-attrs/param-attrs-2018.rs | 0 .../param-attrs-2018.stderr | 0 .../param-attrs-allowed.rs | 0 .../param-attrs-builtin-attrs.rs | 0 .../param-attrs-builtin-attrs.stderr | 0 .../rfc-2565-param-attrs/param-attrs-cfg.rs | 0 .../param-attrs-cfg.stderr | 0 .../param-attrs-pretty.rs | 0 .../proc-macro-cannot-be-used.rs | 0 .../proc-macro-cannot-be-used.stderr | 0 .../rfc-2627-raw-dylib/dlltool-failed.rs | 0 .../rfc-2627-raw-dylib/dlltool-failed.stderr | 0 .../import-name-type-invalid-format.rs | 0 .../import-name-type-invalid-format.stderr | 0 .../import-name-type-multiple.rs | 0 .../import-name-type-multiple.stderr | 0 .../import-name-type-unknown-value.rs | 0 .../import-name-type-unknown-value.stderr | 0 .../import-name-type-unsupported-link-kind.rs | 0 ...ort-name-type-unsupported-link-kind.stderr | 0 .../import-name-type-x86-only.rs | 0 .../import-name-type-x86-only.stderr | 0 .../rfc-2627-raw-dylib/invalid-dlltool.rs | 0 .../rfc-2627-raw-dylib/invalid-dlltool.stderr | 0 .../link-ordinal-and-name.rs | 0 .../link-ordinal-and-name.stderr | 0 .../link-ordinal-invalid-format.rs | 0 .../link-ordinal-invalid-format.stderr | 0 .../link-ordinal-missing-argument.rs | 0 .../link-ordinal-missing-argument.stderr | 0 .../link-ordinal-multiple.rs | 0 .../link-ordinal-multiple.stderr | 0 .../link-ordinal-not-foreign-fn.rs | 0 .../link-ordinal-not-foreign-fn.stderr | 0 .../link-ordinal-too-large.rs | 0 .../link-ordinal-too-large.stderr | 0 .../link-ordinal-too-many-arguments.rs | 0 .../link-ordinal-too-many-arguments.stderr | 0 .../link-ordinal-unsupported-link-kind.rs | 0 .../link-ordinal-unsupported-link-kind.stderr | 0 .../multiple-declarations.rs | 0 .../multiple-declarations.stderr | 0 .../raw-dylib-windows-only.rs | 0 .../raw-dylib-windows-only.stderr | 0 .../rfc-2627-raw-dylib/unsupported-abi.rs | 0 .../rfc-2627-raw-dylib/unsupported-abi.stderr | 0 .../assoc-type-const-bound-usage.rs | 0 .../rfc-2632-const-trait-impl/assoc-type.rs | 0 .../assoc-type.stderr | 0 .../rfc-2632-const-trait-impl/attr-misuse.rs | 0 .../attr-misuse.stderr | 0 .../auxiliary/cross-crate.rs | 0 .../auxiliary/staged-api.rs | 0 .../call-const-trait-method-fail.rs | 0 .../call-const-trait-method-fail.stderr | 0 .../call-const-trait-method-pass.rs | 0 .../call-const-trait-method-pass.stderr | 0 .../call-generic-in-impl.rs | 0 .../call-generic-in-impl.stderr | 0 .../call-generic-method-chain.rs | 0 .../call-generic-method-chain.stderr | 0 .../call-generic-method-dup-bound.rs | 0 .../call-generic-method-dup-bound.stderr | 0 .../call-generic-method-fail.rs | 0 .../call-generic-method-fail.stderr | 0 .../call-generic-method-nonconst-bound.rs | 0 .../call-generic-method-nonconst.rs | 0 .../call-generic-method-nonconst.stderr | 0 .../call-generic-method-pass.rs | 0 .../call-generic-method-pass.stderr | 0 .../rfc-2632-const-trait-impl/call.rs | 0 .../const-and-non-const-impl.rs | 0 .../const-and-non-const-impl.stderr | 0 .../const-check-fns-in-const-impl.rs | 0 .../const-check-fns-in-const-impl.stderr | 0 .../const-closure-parse-not-item.rs | 0 .../const-closure-trait-method-fail.rs | 0 .../const-closure-trait-method-fail.stderr | 0 .../const-closure-trait-method.rs | 0 .../const-closures.rs | 0 .../const-default-method-bodies.rs | 0 .../const-default-method-bodies.stderr | 0 .../const-drop-bound.rs | 0 .../const-drop-fail-2.precise.stderr | 0 .../const-drop-fail-2.rs | 0 .../const-drop-fail-2.stderr | 0 .../const-drop-fail-2.stock.stderr | 0 .../const-drop-fail.precise.stderr | 0 .../const-drop-fail.rs | 0 .../const-drop-fail.stock.stderr | 0 .../rfc-2632-const-trait-impl/const-drop.rs | 0 .../const-impl-norecover.rs | 0 .../const-impl-norecover.stderr | 0 .../const-impl-recovery.rs | 0 .../const-impl-recovery.stderr | 0 .../const-impl-requires-const-trait.rs | 0 .../const-impl-requires-const-trait.stderr | 0 .../const-impl-trait.rs | 0 .../const-impl-trait.stderr | 0 .../const_derives/derive-const-gate.rs | 0 .../const_derives/derive-const-gate.stderr | 0 .../derive-const-non-const-type.rs | 0 .../derive-const-non-const-type.stderr | 0 .../const_derives/derive-const-use.rs | 0 .../const_derives/derive-const-use.stderr | 0 .../const_derives/derive-const-with-params.rs | 0 .../derive-const-with-params.stderr | 0 ...ross-crate-default-method-body-is-const.rs | 0 .../cross-crate.gatednc.stderr | 0 .../rfc-2632-const-trait-impl/cross-crate.rs | 0 .../cross-crate.stock.stderr | 0 .../cross-crate.stocknc.stderr | 0 ...ault-method-body-is-const-body-checking.rs | 0 ...-method-body-is-const-body-checking.stderr | 0 ...ault-method-body-is-const-same-trait-ck.rs | 0 ...-method-body-is-const-same-trait-ck.stderr | 0 ...lt-method-body-is-const-with-staged-api.rs | 0 .../do-not-const-check-override.rs | 0 .../do-not-const-check.rs | 0 .../feature-gate.gated.stderr | 0 .../rfc-2632-const-trait-impl/feature-gate.rs | 0 .../feature-gate.stock.stderr | 0 ...function-pointer-does-not-require-const.rs | 0 .../rfc-2632-const-trait-impl/gate.rs | 0 .../rfc-2632-const-trait-impl/gate.stderr | 0 .../generic-bound.rs | 0 .../generic-bound.stderr | 0 .../hir-const-check.rs | 0 .../hir-const-check.stderr | 0 .../impl-tilde-const-trait.rs | 0 .../impl-tilde-const-trait.stderr | 0 .../impl-with-default-fn-fail.rs | 0 .../impl-with-default-fn-fail.stderr | 0 .../impl-with-default-fn-pass.rs | 0 .../inherent-impl-const-bounds.rs | 0 .../inherent-impl.rs | 0 .../inherent-impl.stderr | 0 .../rfc-2632-const-trait-impl/issue-100222.rs | 0 .../rfc-2632-const-trait-impl/issue-102156.rs | 0 .../issue-102156.stderr | 0 .../rfc-2632-const-trait-impl/issue-102985.rs | 0 .../issue-102985.stderr | 0 .../rfc-2632-const-trait-impl/issue-103677.rs | 0 .../rfc-2632-const-trait-impl/issue-79450.rs | 0 .../issue-79450.stderr | 0 .../rfc-2632-const-trait-impl/issue-88155.rs | 0 .../issue-88155.stderr | 0 .../rfc-2632-const-trait-impl/issue-90052.rs | 0 .../issue-90052.stderr | 0 .../rfc-2632-const-trait-impl/issue-92111.rs | 0 .../issue-92230-wf-super-trait-env.rs | 0 .../nested-closure.rs | 0 ...-const-op-const-closure-non-const-outer.rs | 0 ...st-op-const-closure-non-const-outer.stderr | 0 .../non-const-op-in-closure-in-const.rs | 0 .../non-const-op-in-closure-in-const.stderr | 0 ...fault-bound-non-const-specialized-bound.rs | 0 ...t-bound-non-const-specialized-bound.stderr | 0 .../const-default-const-specialized.rs | 0 ...default-impl-non-const-specialized-impl.rs | 0 ...ult-impl-non-const-specialized-impl.stderr | 0 .../specialization/default-keyword.rs | 0 .../issue-95186-specialize-on-tilde-const.rs | 0 ...87-same-trait-bound-different-constness.rs | 0 .../non-const-default-const-specialized.rs | 0 .../specializing-constness-2.rs | 0 .../specializing-constness-2.stderr | 0 .../specializing-constness.rs | 0 .../specializing-constness.stderr | 0 .../staged-api-user-crate.rs | 0 .../staged-api-user-crate.stderr | 0 .../rfc-2632-const-trait-impl/staged-api.rs | 0 .../staged-api.stable.stderr | 0 .../staged-api.unstable.stderr | 0 .../static-const-trait-bound.rs | 0 .../std-impl-gate.gated.stderr | 0 .../std-impl-gate.rs | 0 .../std-impl-gate.stock.stderr | 0 .../super-traits-fail-2.nn.stderr | 0 .../super-traits-fail-2.ny.stderr | 0 .../super-traits-fail-2.rs | 0 .../super-traits-fail-2.yn.stderr | 0 .../super-traits-fail-2.yy.stderr | 0 .../super-traits-fail-3.nn.stderr | 0 .../super-traits-fail-3.ny.stderr | 0 .../super-traits-fail-3.rs | 0 .../super-traits-fail-3.yn.stderr | 0 .../super-traits-fail.rs | 0 .../super-traits-fail.stderr | 0 .../rfc-2632-const-trait-impl/super-traits.rs | 0 .../rfc-2632-const-trait-impl/syntax.rs | 0 .../tilde-const-and-const-params.rs | 0 .../tilde-const-and-const-params.stderr | 0 .../tilde-const-invalid-places.rs | 0 .../tilde-const-invalid-places.stderr | 0 .../tilde-const-syntax.rs | 0 .../rfc-2632-const-trait-impl/tilde-twice.rs | 0 .../tilde-twice.stderr | 0 .../tilde_const_on_impl_bound.rs | 0 .../trait-default-body-stability.rs | 0 .../trait-default-body-stability.stderr | 0 .../trait-method-ptr-in-consts-ice.rs | 0 .../trait-where-clause-const.rs | 0 .../trait-where-clause-const.stderr | 0 .../trait-where-clause-run.rs | 0 .../trait-where-clause-self-referential.rs | 0 .../trait-where-clause.rs | 0 .../trait-where-clause.stderr | 0 .../without-tilde.rs | 0 .../without-tilde.stderr | 0 543 files changed, 145 insertions(+), 145 deletions(-) delete mode 100644 tests/ui/rfc-2005-default-binding-mode/enum.rs delete mode 100644 tests/ui/rfc-2005-default-binding-mode/for.rs delete mode 100644 tests/ui/rfc-2005-default-binding-mode/lit.rs delete mode 100644 tests/ui/rfc-2005-default-binding-mode/slice.rs rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs (100%) rename tests/ui/{ => rfcs}/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr (100%) rename tests/ui/rfcs/{ => rfc-1014-stdout-existential-crisis}/rfc-1014-2.rs (100%) rename tests/ui/rfcs/{ => rfc-1014-stdout-existential-crisis}/rfc-1014.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/feature-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-6804.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs (100%) rename tests/ui/rfcs/{ => rfc-1445-restrict-constants-in-patterns}/rfc1445/eq-allows-match-on-ty-in-macro.rs (100%) rename tests/ui/rfcs/{ => rfc-1445-restrict-constants-in-patterns}/rfc1445/eq-allows-match.rs (100%) rename tests/ui/rfcs/{ => rfc-1623-static}/rfc1623-2.rs (100%) rename tests/ui/rfcs/{ => rfc-1623-static}/rfc1623-2.stderr (100%) rename tests/ui/rfcs/{ => rfc-1623-static}/rfc1623-3.rs (100%) rename tests/ui/rfcs/{ => rfc-1623-static}/rfc1623-3.stderr (100%) rename tests/ui/rfcs/{ => rfc-1623-static}/rfc1623.rs (100%) rename tests/ui/rfcs/{rfc1717 => rfc-1717-dllimport/1717-dllimport}/library-override.rs (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/missing-link-attr.rs (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/missing-link-attr.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/multiple-renames.rs (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/multiple-renames.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/rename-modifiers.rs (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/rename-modifiers.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/rename-to-empty.rs (100%) rename tests/ui/{ => rfcs}/rfc-1717-dllimport/rename-to-empty.stderr (100%) rename tests/ui/rfcs/{rfc1857-drop-order.rs => rfc-1857-stabilize-drop-order/drop-order.rs} (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/issue-103052-1.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/issue-103052-1.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/issue-103052-2.current.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/issue-103052-2.next.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/issue-103052-2.rs (100%) rename tests/ui/{rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs => rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs} (100%) rename tests/ui/rfcs/rfc-1937-termination-trait/{termination-trait-for-box-dyn-error.rs => termination-trait-for-box-dyn-error-ok.rs} (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-for-never.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs (100%) rename tests/ui/{rfc-1937-termination-trait/termination-trait-for-str.rs => rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs} (100%) rename tests/ui/rfcs/rfc-1937-termination-trait/{termination-trait-for-str.rs => termination-trait-for-str-ok.rs} (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-impl-trait.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-impl-trait.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-in-test.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-main-i32.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-main-i32.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-not-satisfied.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs (100%) rename tests/ui/{ => rfcs}/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/borrowck-issue-49631.rs (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/const.stderr (100%) create mode 100644 tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/enum.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/explicit-mut.rs (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/explicit-mut.stderr (100%) create mode 100644 tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/for.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/issue-44912-or.rs (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/issue-44912-or.stderr (100%) create mode 100644 tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/lit.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/no-double-error.rs (100%) rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/no-double-error.stderr (100%) create mode 100644 tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs rename tests/ui/{ => rfcs}/rfc-2005-default-binding-mode/slice.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/auxiliary/enums.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/auxiliary/monovariants.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/auxiliary/structs.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/auxiliary/unstable.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/auxiliary/variants.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/borrowck-exhaustive.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum-as-cast.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum-as-cast.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/invalid-attribute.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/invalid-attribute.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/omitted-patterns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/omitted-patterns.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/stable-omitted-patterns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/struct.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/struct.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/structs_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/coercions.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/coercions.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/patterns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/variant.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/variant.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/variants_fictive_visibility.rs (100%) rename tests/ui/{ => rfcs}/rfc-2008-non-exhaustive/variants_same_crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs (100%) rename tests/ui/{ => rfcs}/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs (100%) rename tests/ui/{ => rfcs}/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/call-chain.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/caller-location-intrinsic.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/const-caller-location.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/diverging-caller-location.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-odd-syntax.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-odd-syntax.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-invalid-abi.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-invalid-abi.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-main.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-main.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-naked.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-naked.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-start.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/error-with-start.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/intrinsic-wrapper.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/macro-declaration.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/mir-inlined-macro.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/only-for-fns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/only-for-fns.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/std-panic-locations.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/track-caller-attribute.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/track-caller-ffi.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/tracked-closure.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/tracked-fn-ptr.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/tracked-trait-impls.rs (100%) rename tests/ui/{ => rfcs}/rfc-2091-track-caller/tracked-trait-obj.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/cross-crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/cross-crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/dont-infer-static.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/dont-infer-static.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/enum.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/enum.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-dyn.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-dyn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-enum.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-enum.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-projection.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-projection.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-struct.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-struct.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-union.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/explicit-union.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/issue-54467.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-enum.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-enum.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-regions.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-regions.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-structs.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-structs.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-union.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/nested-union.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/privacy.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/projection.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/projection.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/reference.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/reference.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-enum-not-wf.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-enum-not-wf.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-struct-not-wf.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/regions-struct-not-wf.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/self-dyn.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/self-dyn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/self-structs.rs (100%) rename tests/ui/{ => rfcs}/rfc-2093-infer-outlives/self-structs.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-crate-paths/crate-path-non-absolute.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-crate-paths/crate-path-non-absolute.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-crate-paths/keyword-crate-as-identifier.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-1.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-1.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-2.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-2.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-3.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/non-existent-3.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/not-allowed.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/not-allowed.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/single-segment.rs (100%) rename tests/ui/{ => rfcs}/rfc-2126-extern-absolute-paths/single-segment.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/bindings.rs (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/bindings.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/feature-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/feature-gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/run-pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/typeck.rs (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/typeck.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/warns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2294-if-let-guard/warns.stderr (100%) rename tests/ui/rfcs/{ => rfc-2302-self-struct-ctor}/rfc-2302-self-struct-ctor.rs (100%) rename tests/ui/{rfc-2306 => rfcs/rfc-2306-convert-id}/convert-id-const-with-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs (100%) rename tests/ui/{ => rfcs}/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/incorrect-locations.rs (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/incorrect-locations.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/unstable-feature.rs (100%) rename tests/ui/{ => rfcs}/rfc-2397-do-not-recommend/unstable-feature.stderr (100%) rename tests/ui/rfcs/{rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs => rfc-2421-unreserve-pure-offsetof-sizeof-alignof/offsetof-alignof-sizeof-pure-can-be-used-as-idents.rs} (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/crate_name_nonascii_forbidden-1.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/crate_name_nonascii_forbidden-1.stderr (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/crate_name_nonascii_forbidden-2.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/crate_name_nonascii_forbidden-2.stderr (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/extern_block_nonascii_forbidden.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/extern_block_nonascii_forbidden.stderr (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/idents-normalized.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/mod_file_nonascii_forbidden.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/mod_file_nonascii_forbidden.stderr (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/mod_file_nonascii_with_path_allowed.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/mod_inline_nonascii_allowed.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/no_mangle_nonascii_forbidden.rs (100%) rename tests/ui/{rfc-2457 => rfcs/rfc-2457-non-ascii-idents}/no_mangle_nonascii_forbidden.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ast-pretty-check.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ast-pretty-check.stdout (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ast-validate-guards.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ast-validate-guards.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/chains-without-let.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/chains-without-let.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/disallowed-positions.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/disallowed-positions.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/feature-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/feature-gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/irrefutable-lets.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-88498.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-90722.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-92145.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-93150.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-93150.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/issue-99938.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/no-double-assigments.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/protect-precedences.rs (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/protect-precedences.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2497-if-let-chains/then-else-blocks.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/attr-without-param.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/attr-without-param.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/auxiliary/ident-mac.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/auxiliary/param-attrs.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-2018.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-2018.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-allowed.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-cfg.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-cfg.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/param-attrs-pretty.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs (100%) rename tests/ui/{ => rfcs}/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/dlltool-failed.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/dlltool-failed.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-invalid-format.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-multiple.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-multiple.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-unknown-value.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-x86-only.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/import-name-type-x86-only.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/invalid-dlltool.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/invalid-dlltool.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-and-name.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-and-name.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-multiple.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-multiple.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-too-large.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-too-large.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/multiple-declarations.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/multiple-declarations.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/raw-dylib-windows-only.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/unsupported-abi.rs (100%) rename tests/ui/{ => rfcs}/rfc-2627-raw-dylib/unsupported-abi.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/assoc-type.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/assoc-type.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/attr-misuse.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/attr-misuse.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/auxiliary/staged-api.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-in-impl.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-in-impl.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-chain.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-chain.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-fail.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call-generic-method-pass.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/call.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-and-non-const-impl.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-closure-trait-method.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-closures.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-default-method-bodies.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-default-method-bodies.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail-2.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail-2.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-drop.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-norecover.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-norecover.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-recovery.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-recovery.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-trait.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const-impl-trait.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/cross-crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/cross-crate.stock.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/do-not-const-check-override.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/do-not-const-check.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/feature-gate.gated.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/feature-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/feature-gate.stock.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/gate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/generic-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/generic-bound.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/hir-const-check.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/hir-const-check.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/inherent-impl.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/inherent-impl.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-100222.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-102156.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-102156.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-102985.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-102985.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-103677.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-79450.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-79450.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-88155.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-88155.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-90052.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-90052.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-92111.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/nested-closure.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/default-keyword.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specializing-constness-2.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specializing-constness-2.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specializing-constness.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/specializing-constness.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/staged-api-user-crate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/staged-api-user-crate.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/staged-api.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/staged-api.stable.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/staged-api.unstable.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/static-const-trait-bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/std-impl-gate.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-2.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-3.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits-fail.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/super-traits.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/syntax.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-const-syntax.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-twice.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde-twice.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-default-body-stability.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-default-body-stability.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause-const.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause-const.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause-run.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/trait-where-clause.stderr (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/without-tilde.rs (100%) rename tests/ui/{ => rfcs}/rfc-2632-const-trait-impl/without-tilde.stderr (100%) diff --git a/tests/ui/rfc-2005-default-binding-mode/enum.rs b/tests/ui/rfc-2005-default-binding-mode/enum.rs deleted file mode 100644 index 4e57769d6e24c..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/enum.rs +++ /dev/null @@ -1,22 +0,0 @@ -enum Wrapper { - Wrap(i32), -} - -use Wrapper::Wrap; - -pub fn main() { - let Wrap(x) = &Wrap(3); - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - - - if let Some(x) = &Some(3) { - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - } else { - panic!(); - } - - while let Some(x) = &Some(3) { - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - break; - } -} diff --git a/tests/ui/rfc-2005-default-binding-mode/for.rs b/tests/ui/rfc-2005-default-binding-mode/for.rs deleted file mode 100644 index d6c5a13b1bdb0..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/for.rs +++ /dev/null @@ -1,9 +0,0 @@ -struct Foo {} - -pub fn main() { - let mut tups = vec![(Foo {}, Foo {})]; - // The below desugars to &(ref n, mut m). - for (n, mut m) in &tups { - //~^ ERROR cannot move out of a shared reference - } -} diff --git a/tests/ui/rfc-2005-default-binding-mode/lit.rs b/tests/ui/rfc-2005-default-binding-mode/lit.rs deleted file mode 100644 index ce79cfbdc1a30..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/lit.rs +++ /dev/null @@ -1,24 +0,0 @@ -// FIXME(tschottdorf): we want these to compile, but they don't. - -fn with_str() { - let s: &'static str = "abc"; - - match &s { - "abc" => true, //~ ERROR mismatched types - _ => panic!(), - }; -} - -fn with_bytes() { - let s: &'static [u8] = b"abc"; - - match &s { - b"abc" => true, //~ ERROR mismatched types - _ => panic!(), - }; -} - -pub fn main() { - with_str(); - with_bytes(); -} diff --git a/tests/ui/rfc-2005-default-binding-mode/slice.rs b/tests/ui/rfc-2005-default-binding-mode/slice.rs deleted file mode 100644 index 363a0e3e649d9..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/slice.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn main() { - let sl: &[u8] = b"foo"; - - match sl { //~ ERROR non-exhaustive patterns - [first, remainder @ ..] => {}, - }; -} diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr diff --git a/tests/ui/rfcs/rfc-1014-2.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs similarity index 100% rename from tests/ui/rfcs/rfc-1014-2.rs rename to tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs diff --git a/tests/ui/rfcs/rfc-1014.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs similarity index 100% rename from tests/ui/rfcs/rfc-1014.rs rename to tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs diff --git a/tests/ui/rfcs/rfc1445/eq-allows-match-on-ty-in-macro.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match-on-ty-in-macro.rs similarity index 100% rename from tests/ui/rfcs/rfc1445/eq-allows-match-on-ty-in-macro.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match-on-ty-in-macro.rs diff --git a/tests/ui/rfcs/rfc1445/eq-allows-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match.rs similarity index 100% rename from tests/ui/rfcs/rfc1445/eq-allows-match.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match.rs diff --git a/tests/ui/rfcs/rfc1623-2.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs similarity index 100% rename from tests/ui/rfcs/rfc1623-2.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs diff --git a/tests/ui/rfcs/rfc1623-2.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr similarity index 100% rename from tests/ui/rfcs/rfc1623-2.stderr rename to tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr diff --git a/tests/ui/rfcs/rfc1623-3.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs similarity index 100% rename from tests/ui/rfcs/rfc1623-3.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs diff --git a/tests/ui/rfcs/rfc1623-3.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr similarity index 100% rename from tests/ui/rfcs/rfc1623-3.stderr rename to tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr diff --git a/tests/ui/rfcs/rfc1623.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623.rs similarity index 100% rename from tests/ui/rfcs/rfc1623.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623.rs diff --git a/tests/ui/rfcs/rfc1717/library-override.rs b/tests/ui/rfcs/rfc-1717-dllimport/1717-dllimport/library-override.rs similarity index 100% rename from tests/ui/rfcs/rfc1717/library-override.rs rename to tests/ui/rfcs/rfc-1717-dllimport/1717-dllimport/library-override.rs diff --git a/tests/ui/rfc-1717-dllimport/missing-link-attr.rs b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/missing-link-attr.rs rename to tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs diff --git a/tests/ui/rfc-1717-dllimport/missing-link-attr.stderr b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/missing-link-attr.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.stderr diff --git a/tests/ui/rfc-1717-dllimport/multiple-renames.rs b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/multiple-renames.rs rename to tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs diff --git a/tests/ui/rfc-1717-dllimport/multiple-renames.stderr b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/multiple-renames.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.stderr diff --git a/tests/ui/rfc-1717-dllimport/rename-modifiers.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-modifiers.rs rename to tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs diff --git a/tests/ui/rfc-1717-dllimport/rename-modifiers.stderr b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-modifiers.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr diff --git a/tests/ui/rfc-1717-dllimport/rename-to-empty.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-to-empty.rs rename to tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs diff --git a/tests/ui/rfc-1717-dllimport/rename-to-empty.stderr b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-to-empty.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.stderr diff --git a/tests/ui/rfcs/rfc1857-drop-order.rs b/tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs similarity index 100% rename from tests/ui/rfcs/rfc1857-drop-order.rs rename to tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-1.rs b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-1.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.rs diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-1.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-1.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.current.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.current.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.next.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.next.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.rs b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-ok.rs similarity index 100% rename from tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-ok.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-never.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-never.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-str.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-str.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-ok.rs similarity index 100% rename from tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-ok.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/const.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/const.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/const.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/const.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/const.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/const.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/const.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/const.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs new file mode 100644 index 0000000000000..52fbb90ed541b --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs @@ -0,0 +1,45 @@ +// run-pass +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + println!("{}", *x); + + let Wrap(x) = &mut Wrap(3); + println!("{}", *x); + + if let Some(x) = &Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + *x += 1; + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + *x += 1; + break; + } +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs index 52fbb90ed541b..4e57769d6e24c 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs @@ -1,4 +1,3 @@ -// run-pass enum Wrapper { Wrap(i32), } @@ -7,39 +6,17 @@ use Wrapper::Wrap; pub fn main() { let Wrap(x) = &Wrap(3); - println!("{}", *x); + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - let Wrap(x) = &mut Wrap(3); - println!("{}", *x); if let Some(x) = &Some(3) { - println!("{}", *x); - } else { - panic!(); - } - - if let Some(x) = &mut Some(3) { - println!("{}", *x); - } else { - panic!(); - } - - if let Some(x) = &mut Some(3) { - *x += 1; + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference } else { panic!(); } while let Some(x) = &Some(3) { - println!("{}", *x); - break; - } - while let Some(x) = &mut Some(3) { - println!("{}", *x); - break; - } - while let Some(x) = &mut Some(3) { - *x += 1; + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference break; } } diff --git a/tests/ui/rfc-2005-default-binding-mode/enum.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/enum.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/enum.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/explicit-mut.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/explicit-mut.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/explicit-mut.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/explicit-mut.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs new file mode 100644 index 0000000000000..a5a24a806340f --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs @@ -0,0 +1,20 @@ +// run-pass +pub fn main() { + let mut tups = vec![(0u8, 1u8)]; + + for (n, m) in &tups { + let _: &u8 = n; + let _: &u8 = m; + } + + for (n, m) in &mut tups { + *n += 1; + *m += 2; + } + + assert_eq!(tups, vec![(1u8, 3u8)]); + + for (n, m) in tups { + println!("{} {}", m, n); + } +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs index a5a24a806340f..d6c5a13b1bdb0 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs @@ -1,20 +1,9 @@ -// run-pass -pub fn main() { - let mut tups = vec![(0u8, 1u8)]; - - for (n, m) in &tups { - let _: &u8 = n; - let _: &u8 = m; - } +struct Foo {} - for (n, m) in &mut tups { - *n += 1; - *m += 2; - } - - assert_eq!(tups, vec![(1u8, 3u8)]); - - for (n, m) in tups { - println!("{} {}", m, n); +pub fn main() { + let mut tups = vec![(Foo {}, Foo {})]; + // The below desugars to &(ref n, mut m). + for (n, mut m) in &tups { + //~^ ERROR cannot move out of a shared reference } } diff --git a/tests/ui/rfc-2005-default-binding-mode/for.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/for.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/issue-44912-or.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/issue-44912-or.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs new file mode 100644 index 0000000000000..9379753598eb0 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs @@ -0,0 +1,34 @@ +// run-pass +#![allow(dead_code)] +fn with_u8() { + let s = 5u8; + let r = match &s { + 4 => false, + 5 => true, + _ => false, + }; + assert!(r); +} + +// A string literal isn't mistaken for a non-ref pattern (in which case we'd +// deref `s` and mess things up). +fn with_str() { + let s: &'static str = "abc"; + match s { + "abc" => true, + _ => panic!(), + }; +} + +// Ditto with byte strings. +fn with_bytes() { + let s: &'static [u8] = b"abc"; + match s { + b"abc" => true, + _ => panic!(), + }; +} + +pub fn main() { + with_str(); +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs index 9379753598eb0..ce79cfbdc1a30 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs @@ -1,34 +1,24 @@ -// run-pass -#![allow(dead_code)] -fn with_u8() { - let s = 5u8; - let r = match &s { - 4 => false, - 5 => true, - _ => false, - }; - assert!(r); -} +// FIXME(tschottdorf): we want these to compile, but they don't. -// A string literal isn't mistaken for a non-ref pattern (in which case we'd -// deref `s` and mess things up). fn with_str() { let s: &'static str = "abc"; - match s { - "abc" => true, + + match &s { + "abc" => true, //~ ERROR mismatched types _ => panic!(), }; } -// Ditto with byte strings. fn with_bytes() { let s: &'static [u8] = b"abc"; - match s { - b"abc" => true, + + match &s { + b"abc" => true, //~ ERROR mismatched types _ => panic!(), }; } pub fn main() { with_str(); + with_bytes(); } diff --git a/tests/ui/rfc-2005-default-binding-mode/lit.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/lit.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/lit.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/no-double-error.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/no-double-error.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/no-double-error.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/no-double-error.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs new file mode 100644 index 0000000000000..33229a205f4d8 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs @@ -0,0 +1,25 @@ +// run-pass + +fn slice_pat() { + let sl: &[u8] = b"foo"; + + match sl { + [first, remainder @ ..] => { + let _: &u8 = first; + assert_eq!(first, &b'f'); + assert_eq!(remainder, b"oo"); + } + [] => panic!(), + } +} + +fn slice_pat_omission() { + match &[0, 1, 2] { + [..] => {} + }; +} + +fn main() { + slice_pat(); + slice_pat_omission(); +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs index 33229a205f4d8..363a0e3e649d9 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs @@ -1,25 +1,7 @@ -// run-pass - -fn slice_pat() { +pub fn main() { let sl: &[u8] = b"foo"; - match sl { - [first, remainder @ ..] => { - let _: &u8 = first; - assert_eq!(first, &b'f'); - assert_eq!(remainder, b"oo"); - } - [] => panic!(), - } -} - -fn slice_pat_omission() { - match &[0, 1, 2] { - [..] => {} - }; -} - -fn main() { - slice_pat(); - slice_pat_omission(); + match sl { //~ ERROR non-exhaustive patterns + [first, remainder @ ..] => {}, + }; } diff --git a/tests/ui/rfc-2005-default-binding-mode/slice.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/slice.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/slice.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/enums.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/enums.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/monovariants.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/monovariants.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/monovariants.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/monovariants.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/structs.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/structs.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/structs.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/structs.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/unstable.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/unstable.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/variants.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/variants.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/variants.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/variants.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum-as-cast.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum-as-cast.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/invalid-attribute.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/invalid-attribute.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/omitted-patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/struct.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/struct.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/struct.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/struct.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/struct.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/structs_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/structs_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/variant.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variant.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variant.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/variant.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variant.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/variants_fictive_visibility.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variants_fictive_visibility.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variants_fictive_visibility.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variants_fictive_visibility.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/variants_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variants_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variants_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variants_same_crate.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs diff --git a/tests/ui/rfc-2091-track-caller/call-chain.rs b/tests/ui/rfcs/rfc-2091-track-caller/call-chain.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/call-chain.rs rename to tests/ui/rfcs/rfc-2091-track-caller/call-chain.rs diff --git a/tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs diff --git a/tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr diff --git a/tests/ui/rfc-2091-track-caller/caller-location-intrinsic.rs b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-intrinsic.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-intrinsic.rs rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-intrinsic.rs diff --git a/tests/ui/rfc-2091-track-caller/const-caller-location.rs b/tests/ui/rfcs/rfc-2091-track-caller/const-caller-location.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/const-caller-location.rs rename to tests/ui/rfcs/rfc-2091-track-caller/const-caller-location.rs diff --git a/tests/ui/rfc-2091-track-caller/diverging-caller-location.rs b/tests/ui/rfcs/rfc-2091-track-caller/diverging-caller-location.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/diverging-caller-location.rs rename to tests/ui/rfcs/rfc-2091-track-caller/diverging-caller-location.rs diff --git a/tests/ui/rfc-2091-track-caller/error-odd-syntax.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-odd-syntax.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.rs diff --git a/tests/ui/rfc-2091-track-caller/error-odd-syntax.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-odd-syntax.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-invalid-abi.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-invalid-abi.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-main.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-main.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-main.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-main.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-main.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-main.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-main.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-main.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-naked.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-naked.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-naked.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-start.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-start.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-start.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-start.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr diff --git a/tests/ui/rfc-2091-track-caller/intrinsic-wrapper.rs b/tests/ui/rfcs/rfc-2091-track-caller/intrinsic-wrapper.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/intrinsic-wrapper.rs rename to tests/ui/rfcs/rfc-2091-track-caller/intrinsic-wrapper.rs diff --git a/tests/ui/rfc-2091-track-caller/macro-declaration.rs b/tests/ui/rfcs/rfc-2091-track-caller/macro-declaration.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/macro-declaration.rs rename to tests/ui/rfcs/rfc-2091-track-caller/macro-declaration.rs diff --git a/tests/ui/rfc-2091-track-caller/mir-inlined-macro.rs b/tests/ui/rfcs/rfc-2091-track-caller/mir-inlined-macro.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/mir-inlined-macro.rs rename to tests/ui/rfcs/rfc-2091-track-caller/mir-inlined-macro.rs diff --git a/tests/ui/rfc-2091-track-caller/only-for-fns.rs b/tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/only-for-fns.rs rename to tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.rs diff --git a/tests/ui/rfc-2091-track-caller/only-for-fns.stderr b/tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/only-for-fns.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.stderr diff --git a/tests/ui/rfc-2091-track-caller/pass.rs b/tests/ui/rfcs/rfc-2091-track-caller/pass.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/pass.rs rename to tests/ui/rfcs/rfc-2091-track-caller/pass.rs diff --git a/tests/ui/rfc-2091-track-caller/std-panic-locations.rs b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/std-panic-locations.rs rename to tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs diff --git a/tests/ui/rfc-2091-track-caller/track-caller-attribute.rs b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-attribute.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/track-caller-attribute.rs rename to tests/ui/rfcs/rfc-2091-track-caller/track-caller-attribute.rs diff --git a/tests/ui/rfc-2091-track-caller/track-caller-ffi.rs b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/track-caller-ffi.rs rename to tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-closure.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-closure.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-closure.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-closure.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-fn-ptr.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-fn-ptr.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-trait-impls.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-impls.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-trait-impls.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-impls.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-trait-obj.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-obj.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-trait-obj.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-obj.rs diff --git a/tests/ui/rfc-2093-infer-outlives/cross-crate.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/cross-crate.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.rs diff --git a/tests/ui/rfc-2093-infer-outlives/cross-crate.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/cross-crate.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/dont-infer-static.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/dont-infer-static.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.rs diff --git a/tests/ui/rfc-2093-infer-outlives/dont-infer-static.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/dont-infer-static.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-dyn.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-dyn.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-dyn.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-dyn.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-projection.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-projection.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-projection.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-projection.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-struct.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-struct.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-struct.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-struct.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-union.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-union.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-union.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-union.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/issue-54467.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/issue-54467.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/issue-54467.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/issue-54467.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-regions.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-regions.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-regions.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-regions.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-structs.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-structs.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-structs.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-structs.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-union.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-union.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-union.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-union.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/privacy.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/privacy.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/privacy.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/privacy.rs diff --git a/tests/ui/rfc-2093-infer-outlives/projection.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/projection.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/projection.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/projection.rs diff --git a/tests/ui/rfc-2093-infer-outlives/projection.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/projection.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/projection.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/projection.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/reference.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/reference.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/reference.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/reference.rs diff --git a/tests/ui/rfc-2093-infer-outlives/reference.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/reference.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/reference.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/reference.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/self-dyn.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-dyn.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.rs diff --git a/tests/ui/rfc-2093-infer-outlives/self-dyn.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-dyn.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/self-structs.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-structs.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.rs diff --git a/tests/ui/rfc-2093-infer-outlives/self-structs.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-structs.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.stderr diff --git a/tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.rs b/tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.rs similarity index 100% rename from tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.rs rename to tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.rs diff --git a/tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.stderr b/tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.stderr similarity index 100% rename from tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.stderr rename to tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.stderr diff --git a/tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.rs b/tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.rs similarity index 100% rename from tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.rs rename to tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.rs diff --git a/tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr b/tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr similarity index 100% rename from tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr rename to tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/not-allowed.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/not-allowed.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/single-segment.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/single-segment.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/single-segment.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/single-segment.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/bindings.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/bindings.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/bindings.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/bindings.rs diff --git a/tests/ui/rfc-2294-if-let-guard/bindings.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/bindings.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/bindings.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/bindings.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/feature-gate.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/feature-gate.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/run-pass.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/run-pass.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/run-pass.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/run-pass.rs diff --git a/tests/ui/rfc-2294-if-let-guard/typeck.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/typeck.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs diff --git a/tests/ui/rfc-2294-if-let-guard/typeck.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/typeck.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/typeck.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/warns.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/warns.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs diff --git a/tests/ui/rfc-2294-if-let-guard/warns.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/warns.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr diff --git a/tests/ui/rfcs/rfc-2302-self-struct-ctor.rs b/tests/ui/rfcs/rfc-2302-self-struct-ctor/rfc-2302-self-struct-ctor.rs similarity index 100% rename from tests/ui/rfcs/rfc-2302-self-struct-ctor.rs rename to tests/ui/rfcs/rfc-2302-self-struct-ctor/rfc-2302-self-struct-ctor.rs diff --git a/tests/ui/rfc-2306/convert-id-const-with-gate.rs b/tests/ui/rfcs/rfc-2306-convert-id/convert-id-const-with-gate.rs similarity index 100% rename from tests/ui/rfc-2306/convert-id-const-with-gate.rs rename to tests/ui/rfcs/rfc-2306-convert-id/convert-id-const-with-gate.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/incorrect-locations.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/incorrect-locations.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/incorrect-locations.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/incorrect-locations.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/unstable-feature.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/unstable-feature.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/unstable-feature.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/unstable-feature.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr diff --git a/tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs b/tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof/offsetof-alignof-sizeof-pure-can-be-used-as-idents.rs similarity index 100% rename from tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs rename to tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof/offsetof-alignof-sizeof-pure-can-be-used-as-idents.rs diff --git a/tests/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs similarity index 100% rename from tests/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.rs similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.stderr similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.stderr diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.rs similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.stderr similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.stderr diff --git a/tests/ui/rfc-2457/extern_block_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/extern_block_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/extern_block_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/extern_block_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2457/idents-normalized.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/idents-normalized.rs similarity index 100% rename from tests/ui/rfc-2457/idents-normalized.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/idents-normalized.rs diff --git a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_with_path_allowed.rs similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_with_path_allowed.rs diff --git a/tests/ui/rfc-2457/mod_inline_nonascii_allowed.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_inline_nonascii_allowed.rs similarity index 100% rename from tests/ui/rfc-2457/mod_inline_nonascii_allowed.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_inline_nonascii_allowed.rs diff --git a/tests/ui/rfc-2457/no_mangle_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/no_mangle_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-pretty-check.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-pretty-check.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/chains-without-let.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/chains-without-let.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.rs diff --git a/tests/ui/rfc-2497-if-let-chains/chains-without-let.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/chains-without-let.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/disallowed-positions.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs diff --git a/tests/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/disallowed-positions.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/feature-gate.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/feature-gate.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs diff --git a/tests/ui/rfc-2497-if-let-chains/feature-gate.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/feature-gate.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs diff --git a/tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/irrefutable-lets.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/irrefutable-lets.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-88498.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-88498.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-88498.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-88498.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-90722.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-90722.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-92145.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-92145.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-93150.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-93150.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-93150.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-93150.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/issue-99938.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-99938.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs diff --git a/tests/ui/rfc-2497-if-let-chains/no-double-assigments.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/no-double-assigments.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/no-double-assigments.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/no-double-assigments.rs diff --git a/tests/ui/rfc-2497-if-let-chains/protect-precedences.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/protect-precedences.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.rs diff --git a/tests/ui/rfc-2497-if-let-chains/protect-precedences.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/protect-precedences.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/then-else-blocks.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/then-else-blocks.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/then-else-blocks.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/then-else-blocks.rs diff --git a/tests/ui/rfc-2565-param-attrs/attr-without-param.rs b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/attr-without-param.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs diff --git a/tests/ui/rfc-2565-param-attrs/attr-without-param.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/attr-without-param.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.stderr diff --git a/tests/ui/rfc-2565-param-attrs/auxiliary/ident-mac.rs b/tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/ident-mac.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/auxiliary/ident-mac.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/ident-mac.rs diff --git a/tests/ui/rfc-2565-param-attrs/auxiliary/param-attrs.rs b/tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/param-attrs.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/auxiliary/param-attrs.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/param-attrs.rs diff --git a/tests/ui/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs b/tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-2018.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-2018.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-2018.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-2018.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-allowed.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-allowed.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-allowed.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-allowed.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-cfg.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-cfg.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-pretty.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-pretty.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs diff --git a/tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs b/tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs diff --git a/tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/dlltool-failed.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/dlltool-failed.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs diff --git a/tests/ui/rfc-2627-raw-dylib/dlltool-failed.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/dlltool-failed.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/invalid-dlltool.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/invalid-dlltool.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs diff --git a/tests/ui/rfc-2627-raw-dylib/invalid-dlltool.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/invalid-dlltool.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/multiple-declarations.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/multiple-declarations.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs diff --git a/tests/ui/rfc-2627-raw-dylib/multiple-declarations.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/multiple-declarations.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs diff --git a/tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/unsupported-abi.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs diff --git a/tests/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/unsupported-abi.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/attr-misuse.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/attr-misuse.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/attr-misuse.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/attr-misuse.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closures.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closures.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/do-not-const-check-override.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check-override.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/do-not-const-check-override.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check-override.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/do-not-const-check.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/do-not-const-check.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.gated.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.gated.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/gate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/gate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/gate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/generic-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/generic-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/generic-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/generic-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/hir-const-check.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/hir-const-check.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/hir-const-check.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/hir-const-check.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-100222.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-100222.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102156.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102156.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102156.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102156.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102985.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102985.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102985.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102985.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-103677.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-103677.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-79450.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-79450.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-79450.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-79450.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-88155.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-88155.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-88155.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-88155.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-90052.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-90052.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-90052.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-90052.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-92111.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-92111.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/nested-closure.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/nested-closure.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.stable.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.stable.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.unstable.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.unstable.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/static-const-trait-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/static-const-trait-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/syntax.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/syntax.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/syntax.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-twice.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-twice.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-twice.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-twice.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-run.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-run.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/without-tilde.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/without-tilde.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/without-tilde.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/without-tilde.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.stderr From 18e016f7e05d8d6c81bd68b0c36e4981da8004f6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 29 May 2023 15:43:36 +0000 Subject: [PATCH 711/806] Bless tidy root entry limit --- src/tools/tidy/src/ui_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 01ef4abd3dca3..412c6e3120ab2 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1898; -const ROOT_ENTRY_LIMIT: usize = 891; +const ROOT_ENTRY_LIMIT: usize = 871; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files From 50117af409ada03f7ee895c12e0009060f8ba4c1 Mon Sep 17 00:00:00 2001 From: Nikolay Arhipov Date: Sun, 4 Jun 2023 17:44:43 +0300 Subject: [PATCH 712/806] Std support improvement for ps vita target --- Cargo.lock | 4 +- .../src/spec/armv7_sony_vita_newlibeabihf.rs | 15 ++-- library/std/Cargo.toml | 2 +- library/std/src/os/unix/process.rs | 2 +- library/std/src/sys/unix/fd.rs | 11 ++- library/std/src/sys/unix/fs.rs | 37 ++++++++- library/std/src/sys/unix/mod.rs | 12 +-- library/std/src/sys/unix/net.rs | 11 ++- .../src/sys/unix/thread_parking/pthread.rs | 3 +- .../armv7-sony-vita-newlibeabihf.md | 75 ++++--------------- 10 files changed, 84 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 416a1aea4a010..8284804319033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1902,9 +1902,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.143" +version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs index e2c0808f1fdbf..62c93603621ab 100644 --- a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs @@ -1,15 +1,18 @@ use crate::abi::Endian; -use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions}; /// A base target for PlayStation Vita devices using the VITASDK toolchain (using newlib). /// /// Requires the VITASDK toolchain on the host system. pub fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,-q"]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Wl,-q", "-Wl,--pic-veneer"], + ); Target { - llvm_target: "armv7a-vita-eabihf".into(), + llvm_target: "thumbv7a-vita-eabihf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -18,21 +21,19 @@ pub fn target() -> Target { os: "vita".into(), endian: Endian::Little, c_int_width: "32".into(), - dynamic_linking: false, env: "newlib".into(), vendor: "sony".into(), abi: "eabihf".into(), linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), no_default_libraries: false, cpu: "cortex-a9".into(), - executables: true, families: cvs!["unix"], linker: Some("arm-vita-eabi-gcc".into()), relocation_model: RelocModel::Static, - features: "+v7,+neon".into(), + features: "+v7,+neon,+vfp3,+thumb2,+thumb-mode".into(), pre_link_args, exe_suffix: ".elf".into(), - panic_strategy: PanicStrategy::Abort, + has_thumb_interworking: true, max_atomic_width: Some(64), ..Default::default() }, diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 72d910bf95746..74b61db0c1c59 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -libc = { version = "0.2.143", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.145", default-features = false, features = ['rustc-dep-of-std'], public = true } compiler_builtins = { version = "0.1.92" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 729c63d184f2c..2b40b672d9f0a 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -15,7 +15,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use cfg_if::cfg_if; cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { type UserId = u16; type GroupId = u16; } else if #[cfg(target_os = "nto")] { diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index cb630eede6da0..69c93c9200367 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -402,7 +402,10 @@ impl FileDesc { } } #[cfg(any( - all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))), + all( + target_env = "newlib", + not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) + ), target_os = "solaris", target_os = "illumos", target_os = "emscripten", @@ -424,10 +427,10 @@ impl FileDesc { Ok(()) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to, - // because neither supports spawning processes. + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them supports spawning processes. Ok(()) } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 0e5691d40d176..d2fb6238387a1 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -15,6 +15,7 @@ use crate::mem; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -58,6 +59,7 @@ use libc::fstatat64; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -74,6 +76,7 @@ use libc::readdir64_r; target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -283,6 +286,7 @@ unsafe impl Sync for Dir {} target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita" ))] pub struct DirEntry { dir: Arc, @@ -304,10 +308,16 @@ pub struct DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "vita" + )))] d_type: u8, } @@ -319,6 +329,7 @@ struct dirent64_min { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] pub struct DirEntry { dir: Arc, @@ -520,6 +531,7 @@ impl FileAttr { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "vita", )))] pub fn created(&self) -> io::Result { cfg_has_statx! { @@ -541,6 +553,11 @@ impl FileAttr { currently", )) } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + } } #[cfg(target_os = "nto")] @@ -645,6 +662,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -725,6 +743,7 @@ impl Iterator for ReadDir { continue; } + #[cfg(not(target_os = "vita"))] let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, #[cfg(not(any( @@ -735,6 +754,9 @@ impl Iterator for ReadDir { d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + return Some(Ok(DirEntry { entry, name: name.to_owned(), @@ -752,6 +774,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -842,6 +865,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", ))] pub fn file_type(&self) -> io::Result { self.metadata().map(|m| m.file_type()) @@ -853,6 +877,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", )))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { @@ -939,6 +964,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -951,6 +977,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1543,7 +1570,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, |original| { run_path_with_cstr(link, |link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. @@ -1666,6 +1693,8 @@ fn open_to_and_set_permissions( .truncate(true) .open(to)?; let writer_metadata = writer.metadata()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] if writer_metadata.is_file() { // Set the correct file permissions, in case the file already existed. // Don't set the permissions on already existing non-files like @@ -1844,11 +1873,12 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, and Miri +// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri ))] @@ -1861,6 +1891,7 @@ mod remove_dir_impl { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri )))] diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 54e2f20b31703..24566d96bcd61 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -163,12 +163,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any( - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", - target_os = "vita" - )))] + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break @@ -206,7 +201,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita" )))] static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = crate::sync::atomic::AtomicBool::new(false); @@ -216,7 +210,6 @@ static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita", )))] pub(crate) fn unix_sigpipe_attr_specified() -> bool { UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) @@ -407,6 +400,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] extern "C" {} + } else if #[cfg(target_os = "vita")] { + #[link(name = "pthread", kind = "static", modifiers = "-bundle")] + extern "C" {} } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 39edb136c24fd..ca80cabc3b077 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -454,12 +454,21 @@ impl Socket { Ok(passcred != 0) } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = match nonblocking { + true => 1, + false => 0, + }; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { // FIONBIO is inadequate for sockets on illumos/Solaris, so use the diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 43046ed07b82c..8bf4bae7a3f77 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -123,7 +123,8 @@ impl Parker { target_os = "watchos", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_os = "vita", ))] { addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { diff --git a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md index d75bd92beda73..49eed366dacf0 100644 --- a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md +++ b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md @@ -7,7 +7,7 @@ This tier supports the ARM Cortex A9 processor running on a PlayStation Vita con Rust support for this target is not affiliated with Sony, and is not derived from nor used with any official Sony SDK. -## Designated Developers +## Target maintainers * [@amg98](https://github.com/amg98) * [@nikarh](https://github.com/nikarh) @@ -27,9 +27,9 @@ In order to support some APIs, binaries must be linked against `libc` written for the target, using a linker for the target. These are provided by the VITASDK toolchain. -This target generates binaries in the ELF format. +This target generates binaries in the ELF format with thumb ISA. -## Building +## Building the target Rust does not ship pre-compiled artifacts for this target. You can use `build-std` flag to build binaries with `std`: @@ -37,43 +37,9 @@ Rust does not ship pre-compiled artifacts for this target. You can use `build-st cargo build -Z build-std=std,panic_abort --target=armv7-sony-vita-newlibeabihf --release ``` -## Cross-compilation - -This target can be cross-compiled from `x86_64` on either Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation. - -## Testing - -Currently there is no support to run the rustc test suite for this target. - -## Building and Running Rust Programs +## Building Rust programs -`std` support for this target relies on newlib. In order to work, newlib must be initialized correctly. The easiest way to achieve this with VITASDK newlib implementation is by compiling your program as a staticlib with and exposing your main function from rust to `_init` function in `crt0`. - -Add this to your `Cargo.toml`: - -```toml -[lib] -crate-type = ["staticlib"] - -[profile.release] -panic = 'abort' -lto = true -opt-level = 3 -``` - -Your entrypoint should look roughly like this, `src/lib.rs`: -```rust,ignore,no_run -#[used] -#[export_name = "_newlib_heap_size_user"] -pub static _NEWLIB_HEAP_SIZE_USER: u32 = 100 * 1024 * 1024; // Default heap size is only 32mb, increase it to something suitable for your application - -#[no_mangle] -pub extern "C" fn main() { - println!("Hello, world!"); -} -``` - -To test your developed rust programs on PlayStation Vita, first you must correctly link and package your rust staticlib. These steps can be preformed using tools available in VITASDK, and can be automated using tools like `cargo-make`. +To test your developed rust programs on PlayStation Vita, first you must correctly package your elf. These steps can be preformed using tools available in VITASDK, and can be automated using a tool like `cargo-make`. First, set up environment variables for `VITASDK`, and it's binaries: @@ -88,40 +54,21 @@ Use the example below as a template for your project: [env] TITLE = "Rust Hello World" TITLEID = "RUST00001" -# Add other libs required by your project here -LINKER_LIBS = "-lpthread -lm -lmathneon" # At least a "sce_sys" folder should be place there for app metadata (title, icons, description...) # You can find sample assets for that on $VITASDK/share/gcc-arm-vita-eabi/samples/hello_world/sce_sys/ STATIC_DIR = "static" # Folder where static assets should be placed (sce_sys folder is at $STATIC_DIR/sce_sys) CARGO_TARGET_DIR = { script = ["echo ${CARGO_TARGET_DIR:=target}"] } -RUST_TARGET = "armv7-sony-vita-newlibeabihf" CARGO_OUT_DIR = "${CARGO_TARGET_DIR}/${RUST_TARGET}/release" -TARGET_LINKER = "arm-vita-eabi-gcc" -TARGET_LINKER_FLAGS = "-Wl,-q" - [tasks.build] -description = "Build the project using `cargo` as a static lib." +description = "Build the project using `cargo`." command = "cargo" args = ["build", "-Z", "build-std=std,panic_abort", "--target=armv7-sony-vita-newlibeabihf", "--release"] -[tasks.link] -description = "Build an ELF executable using the `vitasdk` linker." -dependencies = ["build"] -script = [ - """ - ${TARGET_LINKER} ${TARGET_LINKER_FLAGS} \ - -L"${CARGO_OUT_DIR}" \ - -l"${CARGO_MAKE_CRATE_FS_NAME}" \ - ${LINKER_LIBS} \ - -o"${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_NAME}.elf" - """ -] - [tasks.strip] description = "Strip the produced ELF executable." -dependencies = ["link"] +dependencies = ["build"] command = "arm-vita-eabi-strip" args = ["-g", '${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_FS_NAME}.elf'] @@ -194,3 +141,11 @@ script = [ ``` After running the above script, you should be able to get a *.vpk file in the same folder your *.elf executable resides. Now you can pick it and install it on your own PlayStation Vita using, or you can use an [Vita3K](https://vita3k.org/) emulator. + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Cross-compilation + +This target can be cross-compiled from `x86_64` on either Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation. From ac48d49ff8c757ce8380b0ea0dc8048a841db192 Mon Sep 17 00:00:00 2001 From: Nikolay Arhipov Date: Mon, 5 Jun 2023 19:26:04 +0300 Subject: [PATCH 713/806] Simplified bool to int conversion --- library/std/src/sys/unix/net.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index ca80cabc3b077..7258c222a6c19 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -462,10 +462,7 @@ impl Socket { #[cfg(target_os = "vita")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let option = match nonblocking { - true => 1, - false => 0, - }; + let option = nonblocking as libc::c_int; setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) } From 58972d19e7ba591321d6e67d32080a5e5a78e19a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 5 Jun 2023 15:32:23 +0000 Subject: [PATCH 714/806] Merge method, type and const object safety checks --- .../rustc_hir_analysis/src/astconv/mod.rs | 4 + .../src/traits/object_safety.rs | 94 +++++++++---------- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 9e78d9858a6da..95517f0141475 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1643,6 +1643,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + // `dyn Trait` desugars to (not Rust syntax) `dyn Trait where ::Assoc = Foo`. + // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated + // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a + // corresponding `Projection` clause for (projection_bound, _) in &projection_bounds { for def_ids in associated_types.values_mut() { def_ids.remove(&projection_bound.projection_def_id()); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index e61cc19354e0f..b2771915eef8e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -115,15 +115,11 @@ fn object_safety_violations_for_trait( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> Vec { - // Check methods for violations. + // Check assoc items for violations. let mut violations: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn) - .filter_map(|&item| { - object_safety_violation_for_method(tcx, trait_def_id, item) - .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span)) - }) + .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item)) .collect(); // Check the trait itself. @@ -145,30 +141,6 @@ fn object_safety_violations_for_trait( violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); } - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Const) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::AssocConst(ident.name, ident.span) - }), - ); - - if !tcx.features().generic_associated_types_extended { - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) - .filter(|item| item.opt_rpitit_info.is_none()) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::GAT(ident.name, ident.span) - }), - ); - } - debug!( "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", trait_def_id, violations @@ -401,34 +373,54 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { }) } -/// Returns `Some(_)` if this method makes the containing trait not object safe. -fn object_safety_violation_for_method( +/// Returns `Some(_)` if this item makes the containing trait not object safe. +#[instrument(level = "debug", skip(tcx), ret)] +fn object_safety_violation_for_assoc_item( tcx: TyCtxt<'_>, trait_def_id: DefId, - method: ty::AssocItem, -) -> Option<(MethodViolationCode, Span)> { - debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); - // Any method that has a `Self : Sized` requisite is otherwise + item: ty::AssocItem, +) -> Option { + // Any item that has a `Self : Sized` requisite is otherwise // exempt from the regulations. - if generics_require_sized_self(tcx, method.def_id) { + if generics_require_sized_self(tcx, item.def_id) { return None; } - let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); - // Get an accurate span depending on the violation. - violation.map(|v| { - let node = tcx.hir().get_if_local(method.def_id); - let span = match (&v, node) { - (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, - (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span()) + match item.kind { + // Associated consts are never object safe, as they can't have `where` bounds yet at all, + // and associated const bounds in trait objects aren't a thing yet either. + ty::AssocKind::Const => { + Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)) + } + ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| { + let node = tcx.hir().get_if_local(item.def_id); + // Get an accurate span depending on the violation. + let span = match (&v, node) { + (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, + (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) + } + _ => item.ident(tcx).span, + }; + + ObjectSafetyViolation::Method(item.name, v, span) + }), + // Associated types can only be object safe if they have `Self: Sized` bounds. + ty::AssocKind::Type => { + if !tcx.features().generic_associated_types_extended + && !tcx.generics_of(item.def_id).params.is_empty() + && item.opt_rpitit_info.is_none() + { + Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)) + } else { + // We will permit associated types if they are explicitly mentioned in the trait object. + // We can't check this here, as here we only check if it is guaranteed to not be possible. + None } - _ => method.ident(tcx).span, - }; - (v, span) - }) + } + } } /// Returns `Some(_)` if this method cannot be called on a trait From 979379aff720ec20e99eeaef3d015088de0ae93d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 2 Jun 2023 03:05:16 +0000 Subject: [PATCH 715/806] Resolve vars in result from scrape_region_constraints --- .../src/traits/query/type_op/custom.rs | 15 ++++++++++++--- tests/ui/issues/issue-13167.rs | 2 ++ tests/ui/issues/issue-15734.rs | 4 ++-- tests/ui/nll/issue-53119.rs | 2 ++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6d8d2103f3904..8b0973021bcc8 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -5,6 +5,7 @@ use crate::traits::ObligationCtxt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_span::source_map::DUMMY_SP; use rustc_span::Span; @@ -24,9 +25,10 @@ impl CustomTypeOp { } } -impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp +impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp where F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, + R: fmt::Debug + TypeFoldable>, { type Output = R; /// We can't do any custom error reporting for `CustomTypeOp`, so @@ -57,12 +59,16 @@ impl fmt::Debug for CustomTypeOp { /// Executes `op` and then scrapes out all the "old style" region /// constraints that result, creating query-region-constraints. -pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( +pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, name: &'static str, span: Span, -) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> { +) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> +where + R: TypeFoldable>, + Op: super::TypeOp<'tcx, Output = R>, +{ // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the // end of each custom type op, we scrape out the region @@ -91,6 +97,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( } })?; + // Next trait solver performs operations locally, and normalize goals should resolve vars. + let value = infcx.resolve_vars_if_possible(value); + let region_obligations = infcx.take_registered_region_obligations(); let region_constraint_data = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( diff --git a/tests/ui/issues/issue-13167.rs b/tests/ui/issues/issue-13167.rs index 8584c98decf4e..9a9f129ec3ab6 100644 --- a/tests/ui/issues/issue-13167.rs +++ b/tests/ui/issues/issue-13167.rs @@ -1,5 +1,7 @@ // check-pass // pretty-expanded FIXME #23616 +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::slice; diff --git a/tests/ui/issues/issue-15734.rs b/tests/ui/issues/issue-15734.rs index be582060601e7..27410d4c3b08e 100644 --- a/tests/ui/issues/issue-15734.rs +++ b/tests/ui/issues/issue-15734.rs @@ -1,6 +1,6 @@ // run-pass -// If `Index` used an associated type for its output, this test would -// work more smoothly. +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::ops::Index; diff --git a/tests/ui/nll/issue-53119.rs b/tests/ui/nll/issue-53119.rs index 03c9c071c9b10..015b72367f1d7 100644 --- a/tests/ui/nll/issue-53119.rs +++ b/tests/ui/nll/issue-53119.rs @@ -1,4 +1,6 @@ // check-pass +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::ops::Deref; From bbc536d3ac83ad0e3600af2d04701faed63ecd79 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 1 May 2023 05:15:45 +0000 Subject: [PATCH 716/806] Emit an error when RTN is used with ty/ct params --- compiler/rustc_hir_analysis/messages.ftl | 7 +++++ .../rustc_hir_analysis/src/astconv/mod.rs | 26 +++++++++++++++-- compiler/rustc_hir_analysis/src/errors.rs | 18 ++++++++++++ .../return-type-notation/ty-or-ct-params.rs | 20 +++++++++++++ .../ty-or-ct-params.stderr | 29 +++++++++++++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 tests/ui/async-await/return-type-notation/ty-or-ct-params.rs create mode 100644 tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 02d1dfcd1134c..cd6cf36baa44d 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -195,6 +195,13 @@ hir_analysis_return_type_notation_conflicting_bound = hir_analysis_return_type_notation_equality_bound = return type notation is not allowed to use type equality +hir_analysis_return_type_notation_illegal_param_const = + return type notation is not allowed for functions that have const parameters + .label = const parameter declared here +hir_analysis_return_type_notation_illegal_param_type = + return type notation is not allowed for functions that have type parameters + .label = type parameter declared here + hir_analysis_return_type_notation_missing_method = cannot find associated function `{$assoc_name}` for `{$ty_name}` diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3d78ea9aa9ba1..37f1765796111 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1215,6 +1215,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } let projection_ty = if return_type_notation { + let mut emitted_bad_param_err = false; // If we have an method return type bound, then we need to substitute // the method's early bound params with suitable late-bound params. let mut num_bound_vars = candidate.bound_vars().len(); @@ -1230,16 +1231,35 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, ) .into(), - GenericParamDefKind::Type { .. } => tcx - .mk_bound( + GenericParamDefKind::Type { .. } => { + if !emitted_bad_param_err { + tcx.sess.emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Type { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ); + emitted_bad_param_err = true; + } + tcx.mk_bound( ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(num_bound_vars), kind: ty::BoundTyKind::Param(param.def_id, param.name), }, ) - .into(), + .into() + } GenericParamDefKind::Const { .. } => { + if !emitted_bad_param_err { + tcx.sess.emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Const { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ); + emitted_bad_param_err = true; + } let ty = tcx .type_of(param.def_id) .no_bound_vars() diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6e7eb4f6cdcd8..7dce1272f96df 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -857,3 +857,21 @@ pub(crate) enum DropImplPolarity { span: Span, }, } + +#[derive(Diagnostic)] +pub(crate) enum ReturnTypeNotationIllegalParam { + #[diag(hir_analysis_return_type_notation_illegal_param_type)] + Type { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, + #[diag(hir_analysis_return_type_notation_illegal_param_const)] + Const { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, +} diff --git a/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs b/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs new file mode 100644 index 0000000000000..7871a2fed03b8 --- /dev/null +++ b/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs @@ -0,0 +1,20 @@ +// edition: 2021 + +#![feature(async_fn_in_trait, return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Foo { + async fn bar() {} + + async fn baz() {} +} + +fn test() +where + T: Foo, + //~^ ERROR return type notation is not allowed for functions that have const parameters + //~| ERROR return type notation is not allowed for functions that have type parameters +{ +} + +fn main() {} diff --git a/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr b/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr new file mode 100644 index 0000000000000..76928c5d7a3e2 --- /dev/null +++ b/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr @@ -0,0 +1,29 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ty-or-ct-params.rs:3:31 + | +LL | #![feature(async_fn_in_trait, return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: return type notation is not allowed for functions that have type parameters + --> $DIR/ty-or-ct-params.rs:14:12 + | +LL | async fn bar() {} + | - type parameter declared here +... +LL | T: Foo, + | ^^^^^^^^^^^ + +error: return type notation is not allowed for functions that have const parameters + --> $DIR/ty-or-ct-params.rs:14:25 + | +LL | async fn baz() {} + | -------------- const parameter declared here +... +LL | T: Foo, + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + From c91a3a4d0cd178cbc30b87ba08ac9ee29083fa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 5 Jun 2023 21:54:52 +0200 Subject: [PATCH 717/806] Test the PGO/BOLT/LTO optimized x64 Linux compiler on CI --- src/bootstrap/lib.rs | 9 +++- src/bootstrap/test.rs | 10 +++- src/ci/stage-build.py | 115 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a1aaee68c625d..eb121b110d61e 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -222,6 +222,7 @@ pub struct Build { initial_cargo: PathBuf, initial_lld: PathBuf, initial_libdir: PathBuf, + initial_sysroot: PathBuf, // Runtime state filled in later on // C/C++ compilers and archiver for all targets @@ -389,13 +390,16 @@ impl Build { "/dummy".to_string() } else { output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) - }; + } + .trim() + .to_string(); + let initial_libdir = initial_target_dir .parent() .unwrap() .parent() .unwrap() - .strip_prefix(initial_sysroot.trim()) + .strip_prefix(&initial_sysroot) .unwrap() .to_path_buf(); @@ -425,6 +429,7 @@ impl Build { initial_cargo: config.initial_cargo.clone(), initial_lld, initial_libdir, + initial_sysroot: initial_sysroot.into(), local_rebuild: config.local_rebuild, fail_fast: config.cmd.fail_fast(), doc_tests: config.cmd.doc_tests(), diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index eec8c4ad69f23..35ba3dab7649f 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1424,7 +1424,15 @@ note: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--src-base").arg(builder.src.join("tests").join(suite)); cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite)); - cmd.arg("--sysroot-base").arg(builder.sysroot(compiler)); + + // When top stage is 0, that means that we're testing an externally provided compiler. + // In that case we need to use its specific sysroot for tests to pass. + let sysroot = if builder.top_stage == 0 { + builder.initial_sysroot.clone() + } else { + builder.sysroot(compiler).to_path_buf() + }; + cmd.arg("--sysroot-base").arg(sysroot); cmd.arg("--stage-id").arg(stage_id); cmd.arg("--suite").arg(suite); cmd.arg("--mode").arg(mode); diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index 4141296bd422e..91bd137085ee6 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -124,6 +124,12 @@ def llvm_bolt_profile_merged_file(self) -> Path: def metrics_path(self) -> Path: return self.build_root() / "build" / "metrics.json" + def executable_extension(self) -> str: + raise NotImplementedError + + def skipped_tests(self) -> Iterable[str]: + return () + class LinuxPipeline(Pipeline): def checkout_path(self) -> Path: @@ -152,6 +158,13 @@ def build_rustc_perf(self): def supports_bolt(self) -> bool: return True + def executable_extension(self) -> str: + return "" + + def skipped_tests(self) -> Iterable[str]: + # This test fails because of linker errors, as of June 2023. + yield "tests/ui/process/nofile-limit.rs" + class WindowsPipeline(Pipeline): def __init__(self): @@ -211,6 +224,13 @@ def rustc_profile_template_path(self) -> Path: def supports_bolt(self) -> bool: return False + def executable_extension(self) -> str: + return ".exe" + + def skipped_tests(self) -> Iterable[str]: + # This test fails as of June 2023 + yield "tests\\codegen\\vec-shrink-panik.rs" + def get_timestamp() -> float: return time.time() @@ -403,9 +423,9 @@ def delete_directory(path: Path): shutil.rmtree(path) -def unpack_archive(archive: Path): +def unpack_archive(archive: Path, target_dir: Optional[Path] = None): LOGGER.info(f"Unpacking archive `{archive}`") - shutil.unpack_archive(archive) + shutil.unpack_archive(str(archive), extract_dir=str(target_dir) if target_dir is not None else None) def download_file(src: str, target: Path): @@ -455,6 +475,7 @@ def cmd( ) return subprocess.run(args, env=environment, check=True) + class BenchmarkRunner: def run_rustc(self, pipeline: Pipeline): raise NotImplementedError @@ -465,6 +486,7 @@ def run_llvm(self, pipeline: Pipeline): def run_bolt(self, pipeline: Pipeline): raise NotImplementedError + class DefaultBenchmarkRunner(BenchmarkRunner): def run_rustc(self, pipeline: Pipeline): # Here we're profiling the `rustc` frontend, so we also include `Check`. @@ -478,6 +500,7 @@ def run_rustc(self, pipeline: Pipeline): LLVM_PROFILE_FILE=str(pipeline.rustc_profile_template_path()) ) ) + def run_llvm(self, pipeline: Pipeline): run_compiler_benchmarks( pipeline, @@ -494,6 +517,7 @@ def run_bolt(self, pipeline: Pipeline): crates=LLVM_BOLT_CRATES ) + def run_compiler_benchmarks( pipeline: Pipeline, profiles: List[str], @@ -650,10 +674,8 @@ def gather_llvm_profiles(pipeline: Pipeline, runner: BenchmarkRunner): def gather_rustc_profiles(pipeline: Pipeline, runner: BenchmarkRunner): LOGGER.info("Running benchmarks with PGO instrumented rustc") - runner.run_rustc(pipeline) - profile_path = pipeline.rustc_profile_merged_file() LOGGER.info(f"Merging Rustc PGO profiles to {profile_path}") cmd([ @@ -770,6 +792,86 @@ def record_metrics(pipeline: Pipeline, timer: Timer): log_metrics(metrics) +def run_tests(pipeline: Pipeline): + """ + After `dist` is executed, we extract its archived components into a sysroot directory, + and then use that extracted rustc as a stage0 compiler. + Then we run a subset of tests using that compiler, to have a basic smoke test which checks + whether the optimization pipeline hasn't broken something. + """ + build_dir = pipeline.build_root() / "build" + dist_dir = build_dir / "dist" + + def extract_dist_dir(name: str) -> Path: + target_dir = build_dir / "optimized-dist" + target_dir.mkdir(parents=True, exist_ok=True) + unpack_archive(dist_dir / f"{name}.tar.xz", target_dir=target_dir) + extracted_path = target_dir / name + assert extracted_path.is_dir() + return extracted_path + + # Extract rustc, libstd, cargo and src archives to create the optimized sysroot + rustc_dir = extract_dist_dir(f"rustc-nightly-{PGO_HOST}") / "rustc" + libstd_dir = extract_dist_dir(f"rust-std-nightly-{PGO_HOST}") / f"rust-std-{PGO_HOST}" + cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / f"cargo" + extracted_src_dir = extract_dist_dir("rust-src-nightly") / "rust-src" + + # We need to manually copy libstd to the extracted rustc sysroot + shutil.copytree( + libstd_dir / "lib" / "rustlib" / PGO_HOST / "lib", + rustc_dir / "lib" / "rustlib" / PGO_HOST / "lib" + ) + + # Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd + # sources to the extracted sysroot. We need sources available so that `-Zsimulate-remapped-rust-src-base` + # works correctly. + shutil.copytree( + extracted_src_dir / "lib" / "rustlib" / "src", + rustc_dir / "lib" / "rustlib" / "src" + ) + + rustc_path = rustc_dir / "bin" / f"rustc{pipeline.executable_extension()}" + assert rustc_path.is_file() + cargo_path = cargo_dir / "bin" / f"cargo{pipeline.executable_extension()}" + assert cargo_path.is_file() + + config_content = f"""profile = "user" +changelog-seen = 2 + +[build] +rustc = "{rustc_path.as_posix()}" +cargo = "{cargo_path.as_posix()}" + +[llvm] +download-ci-llvm = true +""" + logging.info(f"Using following `config.toml` for running tests:\n{config_content}") + + # Simulate a stage 0 compiler with the extracted optimized dist artifacts. + with open("config.toml", "w") as f: + f.write(config_content) + + args = [ + sys.executable, + pipeline.checkout_path() / "x.py", + "test", + "--stage", "0", + "tests/assembly", + "tests/codegen", + "tests/codegen-units", + "tests/incremental", + "tests/mir-opt", + "tests/pretty", + "tests/run-pass-valgrind", + "tests/ui", + ] + for test_path in pipeline.skipped_tests(): + args.extend(["--exclude", test_path]) + cmd(args=args, env=dict( + COMPILETEST_FORCE_STAGE0="1" + )) + + def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]): # Clear and prepare tmp directory shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True) @@ -844,6 +946,11 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRu cmd(final_build_args) record_metrics(pipeline, stage4) + # Try builds can be in various broken states, so we don't want to gatekeep them with tests + if not is_try_build(): + with timer.section("Run tests"): + run_tests(pipeline) + def run(runner: BenchmarkRunner): logging.basicConfig( From b0eaaca31457672a8022312cc0f89b18ebc9d754 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 5 Jun 2023 20:05:08 +0000 Subject: [PATCH 718/806] Remove redundant InferCtxtExt::fresh_item_substs --- .../rustc_hir_analysis/src/astconv/mod.rs | 37 +------------------ compiler/rustc_hir_typeck/src/method/probe.rs | 5 +-- .../rustc_infer/src/infer/type_variable.rs | 1 - compiler/rustc_middle/src/infer/unify_key.rs | 1 - 4 files changed, 3 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 37f1765796111..8d3c0a4fb9e2b 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -26,10 +26,8 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; @@ -2488,7 +2486,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_substs = infcx.fresh_item_substs(impl_); + let impl_substs = infcx.fresh_substs_for_item(span, impl_); let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs); let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -3775,36 +3773,3 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } - -pub trait InferCtxtExt<'tcx> { - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>; -} - -impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var( - self.tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } -} diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9f3d35a77dc75..b7b11ff8942f9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,7 +9,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -954,7 +953,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { trait_def_id: DefId, ) { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - let trait_substs = self.fresh_item_substs(trait_def_id); + let trait_substs = self.fresh_substs_for_item(self.span, trait_def_id); let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { @@ -1899,7 +1898,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, impl_def_id: DefId, ) -> (ty::EarlyBinder>, SubstsRef<'tcx>) { - (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) + (self.tcx.type_of(impl_def_id), self.fresh_substs_for_item(self.span, impl_def_id)) } /// Replaces late-bound-regions bound by `value` with `'static` using diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index f7ab05b2d498a..9f85f9207e8f2 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -129,7 +129,6 @@ pub enum TypeVariableOriginKind { /// (before it has been determined). // FIXME(eddyb) distinguish upvar inference variables from the rest. ClosureSynthetic, - SubstitutionPlaceholder, AutoDeref, AdjustmentType, diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index a873854f0686a..991b9f01985ca 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -116,7 +116,6 @@ pub enum ConstVariableOriginKind { MiscVariable, ConstInference, ConstParameterDefinition(Symbol, DefId), - SubstitutionPlaceholder, } #[derive(Copy, Clone, Debug)] From 140c011ca6659e608833bdc31b66b0f53d11104e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 5 Jun 2023 20:59:41 +0000 Subject: [PATCH 719/806] Don't mention already set fields --- compiler/rustc_hir_typeck/src/expr.rs | 23 ++++++++++--------- .../drop-track-bad-field-in-fru.stderr | 2 ++ .../issue-42599_available_fields_note.stderr | 2 +- tests/ui/error-codes/E0560.stderr | 2 +- tests/ui/issues/issue-5439.stderr | 2 +- tests/ui/proc-macro/span-preservation.stderr | 2 +- tests/ui/structs/struct-field-cfg.stderr | 2 +- .../ui/structs/struct-fields-shorthand.stderr | 2 +- .../ui/structs/struct-fields-too-many.stderr | 2 +- .../union/union-fields-2.mirunsafeck.stderr | 2 +- .../union/union-fields-2.thirunsafeck.stderr | 2 +- 11 files changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3c5feb1ba513d..f03c7ca44ba5f 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2081,13 +2081,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, _ => { // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = self.suggest_field_name( - variant, - field.ident.name, - skip_fields.collect(), - expr_span, - ) { + let skip_fields: Vec<_> = skip_fields.iter().map(|x| x.ident.name).collect(); + if let Some(field_name) = + self.suggest_field_name(variant, field.ident.name, &skip_fields, expr_span) + { err.span_suggestion( field.ident.span, "a field with a similar name exists", @@ -2108,9 +2105,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("`{ty}` does not have this field"), ); } - let available_field_names = + let mut available_field_names = self.available_field_names(variant, expr_span); - if !available_field_names.is_empty() { + available_field_names + .retain(|name| skip_fields.iter().all(|skip| name != skip)); + if available_field_names.is_empty() { + err.note("all struct fields are already assigned"); + } else { err.note(format!( "available fields are: {}", self.name_series_display(available_field_names) @@ -2130,7 +2131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, variant: &'tcx ty::VariantDef, field: Symbol, - skip: Vec, + skip: &[Symbol], // The span where stability will be checked span: Span, ) -> Option { @@ -2582,7 +2583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { access_span: Span, ) { if let Some(suggested_field_name) = - self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) + self.suggest_field_name(def.non_enum_variant(), field.name, &[], access_span) { err.span_suggestion( field.span, diff --git a/tests/ui/async-await/drop-track-bad-field-in-fru.stderr b/tests/ui/async-await/drop-track-bad-field-in-fru.stderr index 07ab8b3c90353..b49b15db64cee 100644 --- a/tests/ui/async-await/drop-track-bad-field-in-fru.stderr +++ b/tests/ui/async-await/drop-track-bad-field-in-fru.stderr @@ -3,6 +3,8 @@ error[E0559]: variant `Option<_>::None` has no field named `value` | LL | None { value: (), ..Default::default() }.await; | ^^^^^ `Option<_>::None` does not have this field + | + = note: all struct fields are already assigned error[E0277]: `Option<_>` is not a future --> $DIR/drop-track-bad-field-in-fru.rs:7:46 diff --git a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr index dbd9dc1bc404f..c20bbce3f24a9 100644 --- a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -10,7 +10,7 @@ error[E0560]: struct `Demo` has no field named `egregiously_nonexistent_field` LL | Self { secret_integer: 3, egregiously_nonexistent_field: () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Demo` does not have this field | - = note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others + = note: available fields are: `favorite_integer`, `innocently_misspellable`, `another_field`, `yet_another_field`, `always_more_fields`, `and_ever` error[E0609]: no field `inocently_mispellable` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:32:41 diff --git a/tests/ui/error-codes/E0560.stderr b/tests/ui/error-codes/E0560.stderr index 6b634f1855dc1..bb5ce478ae1a7 100644 --- a/tests/ui/error-codes/E0560.stderr +++ b/tests/ui/error-codes/E0560.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Simba` has no field named `father` LL | let s = Simba { mother: 1, father: 0 }; | ^^^^^^ `Simba` does not have this field | - = note: available fields are: `mother` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/issues/issue-5439.stderr b/tests/ui/issues/issue-5439.stderr index dc8f8b878d730..a91e4b31f4bdf 100644 --- a/tests/ui/issues/issue-5439.stderr +++ b/tests/ui/issues/issue-5439.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Foo` has no field named `nonexistent` LL | return Box::new(Foo { nonexistent: self, foo: i }); | ^^^^^^^^^^^ `Foo` does not have this field | - = note: available fields are: `foo` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/proc-macro/span-preservation.stderr b/tests/ui/proc-macro/span-preservation.stderr index 66c68be2f09de..8c15cb9de9823 100644 --- a/tests/ui/proc-macro/span-preservation.stderr +++ b/tests/ui/proc-macro/span-preservation.stderr @@ -32,7 +32,7 @@ error[E0560]: struct `Foo` has no field named `b` LL | let y = Foo { a: 10, b: 10isize }; | ^ `Foo` does not have this field | - = note: available fields are: `a` + = note: all struct fields are already assigned error[E0308]: mismatched types --> $DIR/span-preservation.rs:39:5 diff --git a/tests/ui/structs/struct-field-cfg.stderr b/tests/ui/structs/struct-field-cfg.stderr index 5ec47c093a9b0..2b9ba85ddcb88 100644 --- a/tests/ui/structs/struct-field-cfg.stderr +++ b/tests/ui/structs/struct-field-cfg.stderr @@ -10,7 +10,7 @@ error[E0560]: struct `Foo` has no field named `absent` LL | let _ = Foo { present: (), #[cfg(all())] absent: () }; | ^^^^^^ `Foo` does not have this field | - = note: available fields are: `present` + = note: all struct fields are already assigned error[E0027]: pattern does not mention field `present` --> $DIR/struct-field-cfg.rs:13:9 diff --git a/tests/ui/structs/struct-fields-shorthand.stderr b/tests/ui/structs/struct-fields-shorthand.stderr index a285a392168c7..d89d45b39033a 100644 --- a/tests/ui/structs/struct-fields-shorthand.stderr +++ b/tests/ui/structs/struct-fields-shorthand.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Foo` has no field named `z` LL | x, y, z | ^ `Foo` does not have this field | - = note: available fields are: `x`, `y` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/structs/struct-fields-too-many.stderr b/tests/ui/structs/struct-fields-too-many.stderr index a1b7a7a311080..9342607ebce25 100644 --- a/tests/ui/structs/struct-fields-too-many.stderr +++ b/tests/ui/structs/struct-fields-too-many.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `BuildData` has no field named `bar` LL | bar: 0 | ^^^ `BuildData` does not have this field | - = note: available fields are: `foo` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/union/union-fields-2.mirunsafeck.stderr b/tests/ui/union/union-fields-2.mirunsafeck.stderr index 90ad16402f7db..1157f0c2ae770 100644 --- a/tests/ui/union/union-fields-2.mirunsafeck.stderr +++ b/tests/ui/union/union-fields-2.mirunsafeck.stderr @@ -16,7 +16,7 @@ error[E0560]: union `U` has no field named `c` LL | let u = U { a: 0, b: 1, c: 2 }; | ^ `U` does not have this field | - = note: available fields are: `a`, `b` + = note: all struct fields are already assigned error[E0784]: union expressions should have exactly one field --> $DIR/union-fields-2.rs:13:13 diff --git a/tests/ui/union/union-fields-2.thirunsafeck.stderr b/tests/ui/union/union-fields-2.thirunsafeck.stderr index 90ad16402f7db..1157f0c2ae770 100644 --- a/tests/ui/union/union-fields-2.thirunsafeck.stderr +++ b/tests/ui/union/union-fields-2.thirunsafeck.stderr @@ -16,7 +16,7 @@ error[E0560]: union `U` has no field named `c` LL | let u = U { a: 0, b: 1, c: 2 }; | ^ `U` does not have this field | - = note: available fields are: `a`, `b` + = note: all struct fields are already assigned error[E0784]: union expressions should have exactly one field --> $DIR/union-fields-2.rs:13:13 From 0e9e91a95ace9f23e5371bd97e54fe4567150058 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 5 Jun 2023 21:20:49 +0000 Subject: [PATCH 720/806] Don't mention IMPLIED_BOUNDS_ENTAILMENT if signatures reference error --- .../src/check/compare_impl_item.rs | 2 +- tests/ui/implied-bounds/references-err.rs | 22 +++++++++++++++++++ tests/ui/implied-bounds/references-err.stderr | 9 ++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/ui/implied-bounds/references-err.rs create mode 100644 tests/ui/implied-bounds/references-err.stderr 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 31b89525f15d4..7da30116f88fc 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -302,7 +302,7 @@ fn compare_method_predicate_entailment<'tcx>( return Err(emitted); } - if check_implied_wf == CheckImpliedWfMode::Check { + if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() { // We need to check that the impl's args are well-formed given // the hybrid param-env (impl + trait method where-clauses). ocx.register_obligation(traits::Obligation::new( diff --git a/tests/ui/implied-bounds/references-err.rs b/tests/ui/implied-bounds/references-err.rs new file mode 100644 index 0000000000000..203d512e39812 --- /dev/null +++ b/tests/ui/implied-bounds/references-err.rs @@ -0,0 +1,22 @@ +trait Identity { + type Identity; +} +impl Identity for T { + type Identity = T; +} + +trait Trait { + type Assoc: Identity; + fn tokenize(&self) -> ::Identity; +} + +impl Trait for () { + type Assoc = DoesNotExist; + //~^ ERROR cannot find type `DoesNotExist` in this scope + + fn tokenize(&self) -> ::Identity { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/implied-bounds/references-err.stderr b/tests/ui/implied-bounds/references-err.stderr new file mode 100644 index 0000000000000..6076eea3c75fa --- /dev/null +++ b/tests/ui/implied-bounds/references-err.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `DoesNotExist` in this scope + --> $DIR/references-err.rs:14:18 + | +LL | type Assoc = DoesNotExist; + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. From 57cbe250069e7c8eca6bf1e89c6a6b37c71b790d Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Fri, 2 Jun 2023 11:07:28 -0600 Subject: [PATCH 721/806] cleanup some skip_binder -> subst_identity --- compiler/rustc_middle/src/ty/instance.rs | 2 +- compiler/rustc_mir_transform/src/shim.rs | 2 +- src/librustdoc/clean/blanket_impl.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e641d1ef1be6f..c0d591430f7fc 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -586,7 +586,7 @@ impl<'tcx> Instance<'tcx> { if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { - v.skip_binder() + v.subst_identity() } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index ae726dea94426..5f12f1937c06c 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -647,7 +647,7 @@ fn build_call_shim<'tcx>( let mut sig = if let Some(sig_substs) = sig_substs { sig.subst(tcx, &sig_substs) } else { - sig.skip_binder() + sig.subst_identity() }; if let CallKind::Indirect(fnty) = call_kind { diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 83887cc44b96b..9183fdaa08704 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -104,10 +104,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // the post-inference `trait_ref`, as it's more accurate. trait_: Some(clean_trait_ref_with_bindings( cx, - ty::Binder::dummy(trait_ref.skip_binder()), + ty::Binder::dummy(trait_ref.subst_identity()), ThinVec::new(), )), - for_: clean_middle_ty(ty::Binder::dummy(ty.skip_binder()), cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty.subst_identity()), cx, None), items: cx .tcx .associated_items(impl_def_id) @@ -116,7 +116,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .collect::>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.skip_binder().self_ty()), + ty::Binder::dummy(trait_ref.subst_identity().self_ty()), cx, None, ))), From 467bc9ffd56dc31d6db79820bd07dbdd5f653783 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 5 Jun 2023 15:28:50 -0700 Subject: [PATCH 722/806] diagnostics: do not suggest type name tweaks on type-inferred closure args Fixes #111932 --- compiler/rustc_hir_typeck/src/check.rs | 14 +++++++++- .../rustc_hir_typeck/src/gather_locals.rs | 12 ++++++++- .../src/traits/error_reporting/suggestions.rs | 4 +-- tests/ui/closures/issue-111932.rs | 9 +++++++ tests/ui/closures/issue-111932.stderr | 26 +++++++++++++++++++ tests/ui/unsized-locals/issue-67981.stderr | 5 +--- 6 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 tests/ui/closures/issue-111932.rs create mode 100644 tests/ui/closures/issue-111932.stderr diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index bfabd44bb5792..69ccbf0b58ff3 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -96,7 +96,19 @@ pub(super) fn check_fn<'a, 'tcx>( // for simple cases like `fn foo(x: Trait)`, // where we would error once on the parameter as a whole, and once on the binding `x`. if param.pat.simple_ident().is_none() && !params_can_be_unsized { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); + fcx.require_type_is_sized( + param_ty, + param.pat.span, + // ty_span == binding_span iff this is a closure parameter with no type ascription, + // or if it's an implicit `self` parameter + traits::SizedArgumentType( + if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) { + None + } else { + ty_span + }, + ), + ); } fcx.write_ty(param.hir_id, param_ty); diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 38445f2844052..d9b9b34ba583b 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -129,7 +129,17 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { self.fcx.require_type_is_sized( var_ty, p.span, - traits::SizedArgumentType(Some(ty_span)), + // ty_span == ident.span iff this is a closure parameter with no type + // ascription, or if it's an implicit `self` parameter + traits::SizedArgumentType( + if ty_span == ident.span + && self.fcx.tcx.is_closure(self.fcx.body_id.into()) + { + None + } else { + Some(ty_span) + }, + ), ); } } else { 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 42038dbc3d82e..01f2782fd284e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2807,8 +2807,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.help("unsized locals are gated as an unstable feature"); } } - ObligationCauseCode::SizedArgumentType(sp) => { - if let Some(span) = sp { + ObligationCauseCode::SizedArgumentType(ty_span) => { + if let Some(span) = ty_span { if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() && let ty::Clause::Trait(trait_pred) = clause && let ty::Dynamic(..) = trait_pred.self_ty().kind() diff --git a/tests/ui/closures/issue-111932.rs b/tests/ui/closures/issue-111932.rs new file mode 100644 index 0000000000000..eb3fe08cbc409 --- /dev/null +++ b/tests/ui/closures/issue-111932.rs @@ -0,0 +1,9 @@ +trait Foo: std::fmt::Debug {} + +fn print_foos(foos: impl Iterator) { + foos.for_each(|foo| { //~ ERROR [E0277] + println!("{:?}", foo); //~ ERROR [E0277] + }); +} + +fn main() {} diff --git a/tests/ui/closures/issue-111932.stderr b/tests/ui/closures/issue-111932.stderr new file mode 100644 index 0000000000000..937bdf3bea255 --- /dev/null +++ b/tests/ui/closures/issue-111932.stderr @@ -0,0 +1,26 @@ +error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time + --> $DIR/issue-111932.rs:4:20 + | +LL | foos.for_each(|foo| { + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Foo + 'static)` + = note: all function arguments must have a statically known size + = help: unsized fn params are gated as an unstable feature + +error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time + --> $DIR/issue-111932.rs:5:26 + | +LL | println!("{:?}", foo); + | ---- ^^^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `dyn Foo` +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_debug` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unsized-locals/issue-67981.stderr b/tests/ui/unsized-locals/issue-67981.stderr index a4b179ae2fd12..13fdc037ac569 100644 --- a/tests/ui/unsized-locals/issue-67981.stderr +++ b/tests/ui/unsized-locals/issue-67981.stderr @@ -5,10 +5,7 @@ LL | let f: fn([u8]) = |_| {}; | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` -help: function arguments must have a statically known size, borrowed types always have a known size - | -LL | let f: fn([u8]) = |&_| {}; - | + + = note: all function arguments must have a statically known size error: aborting due to previous error From fd3d2d49f2527efd2decad3a6194b82e26137bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 6 Jun 2023 04:49:09 +0200 Subject: [PATCH 723/806] Don't hold the active queries lock while calling `make_query` --- compiler/rustc_query_system/src/query/plumbing.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 730e4c8d30db3..b2bc33c7e0de0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -69,6 +69,8 @@ where make_query: fn(Qcx, K) -> QueryStackFrame, jobs: &mut QueryMap, ) -> Option<()> { + let mut active = Vec::new(); + #[cfg(parallel_compiler)] { // We use try_lock_shards here since we are called from the @@ -77,8 +79,7 @@ where for shard in shards.iter() { for (k, v) in shard.iter() { if let QueryResult::Started(ref job) = *v { - let query = make_query(qcx, *k); - jobs.insert(job.id, QueryJobInfo { query, job: job.clone() }); + active.push((*k, job.clone())); } } } @@ -91,12 +92,18 @@ where // really hurt much.) for (k, v) in self.active.try_lock()?.iter() { if let QueryResult::Started(ref job) = *v { - let query = make_query(qcx, *k); - jobs.insert(job.id, QueryJobInfo { query, job: job.clone() }); + active.push((*k, job.clone())); } } } + // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call + // queries leading to a deadlock. + for (key, job) in active { + let query = make_query(qcx, key); + jobs.insert(job.id, QueryJobInfo { query, job }); + } + Some(()) } } From bd32075934ca2825e6b51d246e19670941598240 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 8 May 2023 19:39:16 +0800 Subject: [PATCH 724/806] Add new Tier-3 targets: `loongarch64-unknown-none*` MCP: https://github.com/rust-lang/compiler-team/issues/628 --- .../src/spec/loongarch64_unknown_none.rs | 23 ++++++ .../loongarch64_unknown_none_softfloat.rs | 24 ++++++ compiler/rustc_target/src/spec/mod.rs | 3 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 2 + .../src/platform-support/loongarch-none.md | 79 +++++++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 compiler/rustc_target/src/spec/loongarch64_unknown_none.rs create mode 100644 compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs create mode 100644 src/doc/rustc/src/platform-support/loongarch-none.md diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs new file mode 100644 index 0000000000000..618250591ad88 --- /dev/null +++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs @@ -0,0 +1,23 @@ +use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-none".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "+f,+d".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + position_independent_executables: true, + static_position_independent_executables: true, + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Small), + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs new file mode 100644 index 0000000000000..23123d7630c8b --- /dev/null +++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs @@ -0,0 +1,24 @@ +use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-none-softfloat".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "-f,-d".into(), + abi: "softfloat".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + llvm_abiname: "lp64s".into(), + max_atomic_width: Some(64), + position_independent_executables: true, + static_position_independent_executables: true, + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Small), + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 05cb7e87a9365..0a9a50652f528 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1293,6 +1293,9 @@ supported_targets! { ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), ("riscv64gc-unknown-linux-musl", riscv64gc_unknown_linux_musl), + ("loongarch64-unknown-none", loongarch64_unknown_none), + ("loongarch64-unknown-none-softfloat", loongarch64_unknown_none_softfloat), + ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 73343ba9df51b..2b0431a159b1b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -31,6 +31,7 @@ - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) + - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3b2463aa5b2dd..5a7da882af019 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -267,6 +267,8 @@ target | std | host | notes `i686-uwp-windows-gnu` | ? | | `i686-uwp-windows-msvc` | ? | | `i686-wrs-vxworks` | ? | | +[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) +[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux MUSL diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md new file mode 100644 index 0000000000000..d0ae3425fa8eb --- /dev/null +++ b/src/doc/rustc/src/platform-support/loongarch-none.md @@ -0,0 +1,79 @@ +# `loongarch*-unknown-none*` + +**Tier: 3** + +Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, etc. + +| Target | Descriptions | +|------------------------------------|-------------------------------------------------------| +| loongarch64-unknown-none | LoongArch 64-bit, LP64D ABI (freestanding, hardfloat) | +| loongarch64-unknown-none-softfloat | LoongArch 64-bit, LP64S ABI (freestanding, softfloat) | + +## Target maintainers + +- [WANG Rui](https://github.com/heiher) `wangrui@loongson.cn` +- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name` + +## Requirements + +This target is cross-compiled. There is no support for `std`. There is no +default allocator, but it's possible to use `alloc` by supplying an allocator. + +This allows the generated code to run in environments, such as kernels, which +may need to avoid the use of such registers or which may have special considerations +about the use of such registers (e.g. saving and restoring them to avoid breaking +userspace code using the same registers). You can change code generation to use +additional CPU features via the `-C target-feature=` codegen options to rustc, or +via the `#[target_feature]` mechanism within Rust code. + +By default, code generated with this target should run on any `loongarch` +hardware; enabling additional target features may raise this baseline. + +Code generated with this target will use the `small` code model by default. +You can change this using the `-C code-model=` option to rustc. + +On `loongarch64-unknown-none*`, `extern "C"` uses the [standard calling +convention](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html). + +This target generates binaries in the ELF format. Any alternate formats or +special considerations for binary layout will require linker options or linker +scripts. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `config.toml`: + +```toml +[build] +build-stage = 1 +target = ["loongarch64-unknown-none"] +``` + +## Building Rust programs + +```text +# target flag may be used with any cargo or rustc command +cargo build --target loongarch64-unknown-none +``` + +## Testing + +As `loongarch64-unknown-none*` supports a variety of different environments and does +not support `std`, this target does not support running the Rust test suite. + +## Cross-compilation toolchains and C code + +If you want to compile C code along with Rust (such as for Rust crates with C +dependencies), you will need an appropriate `loongarch` toolchain. + +Rust *may* be able to use an `loongarch64-unknown-linux-gnu-` toolchain with +appropriate standalone flags to build for this toolchain (depending on the assumptions +of that toolchain, see below), or you may wish to use a separate +`loongarch64-unknown-none` toolchain. + +On some `loongarch` hosts that use ELF binaries, you *may* be able to use the host +C toolchain, if it does not introduce assumptions about the host environment +that don't match the expectations of a standalone environment. Otherwise, you +may need a separate toolchain for standalone/freestanding development, just as +when cross-compiling from a non-`loongarch` platform. From aaca1712dbd5c674b6d05c539c4c2e630d38edfc Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 6 Jun 2023 03:15:52 +0000 Subject: [PATCH 725/806] Add myself to highfive rotation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index b1f4e9c77fbd5..d20bec883980d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -496,6 +496,7 @@ compiler-team-contributors = [ "@TaKO8Ki", "@WaffleLapkin", "@b-naber", + "@fee1-dead", ] compiler = [ "compiler-team", From a3cc503876a761871674d27ce1bb7a35fd3d9900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 6 Jun 2023 10:33:32 +0300 Subject: [PATCH 726/806] Fix rust-analyzer proc macro server --- src/bootstrap/tool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 0f0a3bb8775db..962cbf758d4d5 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -711,7 +711,7 @@ impl Step for RustAnalyzerProcMacroSrv { tool: "rust-analyzer-proc-macro-srv", mode: Mode::ToolStd, path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli", - extra_features: vec!["proc-macro-srv/sysroot-abi".to_owned()], + extra_features: vec!["sysroot-abi".to_owned()], is_optional_tool: false, source_type: SourceType::InTree, allow_features: RustAnalyzer::ALLOW_FEATURES, From 01aaad3f1fd224d3761c2d3b78469c5ed9db7ff5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 6 Jun 2023 09:37:30 +0200 Subject: [PATCH 727/806] remove `has_error_field` helper method --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 8 +++++--- compiler/rustc_middle/src/ty/util.rs | 12 ------------ ...st-param-type-depends-on-type-param.full.stderr | 14 ++------------ ...nst-param-type-depends-on-type-param.min.stderr | 14 ++------------ .../const-param-type-depends-on-type-param.rs | 1 - 5 files changed, 9 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 69e32c35ed8d3..a269a7a1d8b1e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1745,9 +1745,11 @@ fn check_variances_for_type_defn<'tcx>( item: &hir::Item<'tcx>, hir_generics: &hir::Generics<'_>, ) { - let ty = tcx.type_of(item.owner_id).subst_identity(); - if tcx.has_error_field(ty) { - return; + let identity_substs = ty::InternalSubsts::identity_for_item(tcx, item.owner_id); + for field in tcx.adt_def(item.owner_id).all_fields() { + if field.ty(tcx, identity_substs).references_error() { + return; + } } let ty_predicates = tcx.predicates_of(item.owner_id); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index dce2f5545f51d..d45e4d595a7cf 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -173,18 +173,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { - if let ty::Adt(def, substs) = *ty.kind() { - for field in def.all_fields() { - let field_ty = field.ty(self, substs); - if let ty::Error(_) = field_ty.kind() { - return true; - } - } - } - false - } - /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr index 938fb08b7956e..e508890dd7c8b 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr @@ -6,16 +6,6 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:11:22 - | -LL | pub struct Dependent([(); X]); - | ^ unused parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0392, E0770. -For more information about an error, try `rustc --explain E0392`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr index 938fb08b7956e..e508890dd7c8b 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr @@ -6,16 +6,6 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:11:22 - | -LL | pub struct Dependent([(); X]); - | ^ unused parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0392, E0770. -For more information about an error, try `rustc --explain E0392`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs index 71d91fd7e7f45..fc3aa9cbc27c9 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -10,6 +10,5 @@ pub struct Dependent([(); X]); //~^ ERROR: the type of const parameters must not depend on other generic parameters -//~| ERROR: parameter `T` is never used fn main() {} From 4711c3078a653ab7242ed5b9e954ae7ad03646f3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 6 Jun 2023 11:50:04 +0200 Subject: [PATCH 728/806] Prevent emitting `missing_docs` for `pub extern crate` --- compiler/rustc_lint/src/builtin.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 213e8db66a056..8c0956a618dea 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -548,8 +548,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { // Previously the Impl and Use types have been excluded from missing docs, - // so we will continue to exclude them for compatibility - if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) = it.kind { + // so we will continue to exclude them for compatibility. + // + // The documentation on `ExternCrate` is not used at the moment so no need to warn for it. + if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(_) = + it.kind + { return; } From 550fe634bf5914e01ea529119bd23bf276fe673b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 6 Jun 2023 11:50:24 +0200 Subject: [PATCH 729/806] Add regression test for #112308 --- tests/ui/lint/auxiliary/missing_docs.rs | 1 + tests/ui/lint/lint-missing-doc.rs | 4 ++ tests/ui/lint/lint-missing-doc.stderr | 52 ++++++++++++------------- 3 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 tests/ui/lint/auxiliary/missing_docs.rs diff --git a/tests/ui/lint/auxiliary/missing_docs.rs b/tests/ui/lint/auxiliary/missing_docs.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/ui/lint/auxiliary/missing_docs.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/ui/lint/lint-missing-doc.rs b/tests/ui/lint/lint-missing-doc.rs index e4c9f8f559b18..b59f2212f51b3 100644 --- a/tests/ui/lint/lint-missing-doc.rs +++ b/tests/ui/lint/lint-missing-doc.rs @@ -1,5 +1,6 @@ // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. +// aux-build:missing_docs.rs #![deny(missing_docs)] #![allow(dead_code)] #![feature(associated_type_defaults, extern_types)] @@ -8,6 +9,9 @@ //! Some garbage docs for the crate here #![doc="More garbage"] +// There should be not "missing_docs" warning on "pub extern crate". +pub extern crate missing_docs; + type Typedef = String; pub type PubTypedef = String; //~ ERROR: missing documentation for a type alias diff --git a/tests/ui/lint/lint-missing-doc.stderr b/tests/ui/lint/lint-missing-doc.stderr index c94bd3b8dfb3f..adcc21c44b262 100644 --- a/tests/ui/lint/lint-missing-doc.stderr +++ b/tests/ui/lint/lint-missing-doc.stderr @@ -1,155 +1,155 @@ error: missing documentation for a type alias - --> $DIR/lint-missing-doc.rs:12:1 + --> $DIR/lint-missing-doc.rs:16:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-missing-doc.rs:3:9 + --> $DIR/lint-missing-doc.rs:4:9 | LL | #![deny(missing_docs)] | ^^^^^^^^^^^^ error: missing documentation for a struct - --> $DIR/lint-missing-doc.rs:19:1 + --> $DIR/lint-missing-doc.rs:23:1 | LL | pub struct PubFoo { | ^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/lint-missing-doc.rs:20:5 + --> $DIR/lint-missing-doc.rs:24:5 | LL | pub a: isize, | ^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/lint-missing-doc.rs:31:1 + --> $DIR/lint-missing-doc.rs:35:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:35:1 + --> $DIR/lint-missing-doc.rs:39:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^ error: missing documentation for a trait - --> $DIR/lint-missing-doc.rs:53:1 + --> $DIR/lint-missing-doc.rs:57:1 | LL | pub trait C { | ^^^^^^^^^^^ error: missing documentation for a method - --> $DIR/lint-missing-doc.rs:54:5 + --> $DIR/lint-missing-doc.rs:58:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ error: missing documentation for a method - --> $DIR/lint-missing-doc.rs:55:5 + --> $DIR/lint-missing-doc.rs:59:5 | LL | fn foo_with_impl(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:56:5 + --> $DIR/lint-missing-doc.rs:60:5 | LL | fn foo_no_self(); | ^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:57:5 + --> $DIR/lint-missing-doc.rs:61:5 | LL | fn foo_no_self_with_impl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated type - --> $DIR/lint-missing-doc.rs:67:5 + --> $DIR/lint-missing-doc.rs:71:5 | LL | type AssociatedType; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated type - --> $DIR/lint-missing-doc.rs:68:5 + --> $DIR/lint-missing-doc.rs:72:5 | LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:84:5 + --> $DIR/lint-missing-doc.rs:88:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/lint-missing-doc.rs:121:1 + --> $DIR/lint-missing-doc.rs:125:1 | LL | pub enum PubBaz { | ^^^^^^^^^^^^^^^ error: missing documentation for a variant - --> $DIR/lint-missing-doc.rs:122:5 + --> $DIR/lint-missing-doc.rs:126:5 | LL | PubBazA { | ^^^^^^^ error: missing documentation for a struct field - --> $DIR/lint-missing-doc.rs:123:9 + --> $DIR/lint-missing-doc.rs:127:9 | LL | a: isize, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/lint-missing-doc.rs:154:1 + --> $DIR/lint-missing-doc.rs:158:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/lint-missing-doc.rs:164:1 + --> $DIR/lint-missing-doc.rs:168:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:170:5 + --> $DIR/lint-missing-doc.rs:174:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:171:5 + --> $DIR/lint-missing-doc.rs:175:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:177:9 + --> $DIR/lint-missing-doc.rs:181:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:192:5 + --> $DIR/lint-missing-doc.rs:196:5 | LL | pub fn extern_fn_undocumented(f: f32) -> f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/lint-missing-doc.rs:197:5 + --> $DIR/lint-missing-doc.rs:201:5 | LL | pub static EXTERN_STATIC_UNDOCUMENTED: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a foreign type - --> $DIR/lint-missing-doc.rs:202:5 + --> $DIR/lint-missing-doc.rs:206:5 | LL | pub type ExternTyUndocumented; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a trait alias - --> $DIR/lint-missing-doc.rs:206:1 + --> $DIR/lint-missing-doc.rs:210:1 | LL | pub trait T = Sync; | ^^^^^^^^^^^ From 032857e7e403f654129c45dc7e6718a9ad49e377 Mon Sep 17 00:00:00 2001 From: Nikolay Arhipov Date: Tue, 6 Jun 2023 16:09:05 +0300 Subject: [PATCH 730/806] Bumped libc version --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8284804319033..0355c45d7e94a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1902,9 +1902,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.145" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 74b61db0c1c59..4b52894c509da 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -libc = { version = "0.2.145", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true } compiler_builtins = { version = "0.1.92" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } From c927743b7b7bd382836dcce2d1140a7e829dc3d0 Mon Sep 17 00:00:00 2001 From: bohan Date: Tue, 6 Jun 2023 23:11:08 +0800 Subject: [PATCH 731/806] fix(expand): prevent infinity loop in macro containing only "///" --- compiler/rustc_expand/src/mbe/macro_parser.rs | 1 + compiler/rustc_expand/src/mbe/macro_rules.rs | 1 + tests/ui/macros/issue-112342-1.rs | 36 +++++++++++++++++++ tests/ui/macros/issue-112342-1.stderr | 29 +++++++++++++++ tests/ui/macros/issue-112342-2.rs | 28 +++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 tests/ui/macros/issue-112342-1.rs create mode 100644 tests/ui/macros/issue-112342-1.stderr create mode 100644 tests/ui/macros/issue-112342-2.rs diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 1c222fb4a898c..f0e67cfd50e08 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec { } /// A single matcher position, representing the state of matching. +#[derive(Debug)] struct MatcherPos { /// The index into `TtParser::locs`, which represents the "dot". idx: usize, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e4c65a2049bf6..576d636d489c2 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -647,6 +647,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { if seq.separator.is_none() && seq.tts.iter().all(|seq_tt| match seq_tt { TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, + TokenTree::Token(t) => matches!(t, Token { kind: DocComment(..), .. }), TokenTree::Sequence(_, sub_seq) => { sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne diff --git a/tests/ui/macros/issue-112342-1.rs b/tests/ui/macros/issue-112342-1.rs new file mode 100644 index 0000000000000..14fe9bbd97a24 --- /dev/null +++ b/tests/ui/macros/issue-112342-1.rs @@ -0,0 +1,36 @@ +// same as #95267, ignore doc comment although it's a bug. + +macro_rules! m1 { + ( + $( + /// + )* + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m1! {} + +macro_rules! m2 { + ( + $( + /// + )+ + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m2! {} + +macro_rules! m3 { + ( + $( + /// + )? + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m3! {} + +fn main() {} diff --git a/tests/ui/macros/issue-112342-1.stderr b/tests/ui/macros/issue-112342-1.stderr new file mode 100644 index 0000000000000..1ba7c0ffd3bd1 --- /dev/null +++ b/tests/ui/macros/issue-112342-1.stderr @@ -0,0 +1,29 @@ +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:5:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )* + | |_________^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:16:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )+ + | |_________^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:27:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )? + | |_________^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/issue-112342-2.rs b/tests/ui/macros/issue-112342-2.rs new file mode 100644 index 0000000000000..1e1d953fa5269 --- /dev/null +++ b/tests/ui/macros/issue-112342-2.rs @@ -0,0 +1,28 @@ +// check-pass + +// same as #95267, ignore doc comment although it's a bug. + +macro_rules! m1 { + ( + $( + /// + $expr: expr, + )* + ) => {}; +} + +m1! {} + +macro_rules! m2 { + ( + $( + /// + $expr: expr, + /// + )* + ) => {}; +} + +m2! {} + +fn main() {} From e0acff796a9221bc4d6769e3db5bf158647ef0e1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 31 May 2023 01:02:32 +0000 Subject: [PATCH 732/806] New trait solver is a property of inference context --- .../src/region_infer/opaque_types.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_hir_analysis/src/autoderef.rs | 2 +- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_hir_typeck/src/writeback.rs | 4 +-- compiler/rustc_infer/src/infer/at.rs | 1 + compiler/rustc_infer/src/infer/combine.rs | 34 +++++++++---------- compiler/rustc_infer/src/infer/equate.rs | 2 +- compiler/rustc_infer/src/infer/lattice.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 17 ++++++++++ .../rustc_infer/src/infer/nll_relate/mod.rs | 6 ++-- .../rustc_infer/src/infer/opaque_types.rs | 6 ++-- compiler/rustc_infer/src/infer/projection.rs | 2 +- compiler/rustc_infer/src/infer/sub.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- .../src/solve/eval_ctxt.rs | 1 + .../src/traits/error_reporting/mod.rs | 2 +- .../src/traits/query/evaluate_obligation.rs | 2 +- .../src/traits/query/type_op/mod.rs | 2 +- .../src/traits/select/mod.rs | 4 +-- .../src/traits/structural_normalize.rs | 2 +- .../rustc_traits/src/evaluate_obligation.rs | 2 +- 23 files changed, 61 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 13e346b86bce3..a561496b02639 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -285,7 +285,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let infcx = self .tcx .infer_ctxt() - .with_opaque_type_inference(if self.tcx.trait_solver_next() { + .with_opaque_type_inference(if self.next_trait_solver() { DefiningAnchor::Bind(def_id) } else { DefiningAnchor::Bubble diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 908ff3da5ca0b..a2f95c19cf59f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -188,7 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>( // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering // predefined opaques in the typeck root. - if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) { + if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { checker.register_predefined_opaques_in_new_solver(); } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index d6d1498d708ed..f624118a4f1f0 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -73,7 +73,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { // NOTE: we may still need to normalize the built-in deref in case // we have some type like `&::Assoc`, since users of // autoderef expect this type to have been structurally normalized. - if self.infcx.tcx.trait_solver_next() + if self.infcx.next_trait_solver() && let ty::Alias(ty::Projection, _) = ty.kind() { let (normalized_ty, obligations) = self.structurally_normalize(ty)?; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index ba49e0c41618a..81231e8fe068b 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -156,7 +156,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // In the new solver, lazy norm may allow us to shallowly equate // more types, but we emit possibly impossible-to-satisfy obligations. // Filter these cases out to make sure our coercion is more accurate. - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { if let Ok(res) = &res { for obligation in &res.obligations { if !self.predicate_may_hold(&obligation) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 38ddb7e760410..fb56b7e74cb54 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1476,7 +1476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let mut ty = self.resolve_vars_with_obligations(ty); - if self.tcx.trait_solver_next() + if self.next_trait_solver() && let ty::Alias(ty::Projection, _) = ty.kind() { match self diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 964acc4eb77ef..6a3a46c778a7a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -591,7 +591,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .insert(opaque_type_key, hidden_type) && last_opaque_ty.ty != hidden_type.ty { - assert!(!self.tcx().trait_solver_next()); + assert!(!self.fcx.next_trait_solver()); hidden_type .report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx()) .stash( @@ -812,7 +812,7 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match self.fcx.fully_resolve(t) { - Ok(t) if self.fcx.tcx.trait_solver_next() => { + Ok(t) if self.fcx.next_trait_solver() => { // We must normalize erasing regions here, since later lints // expect that types that show up in the typeck are fully // normalized. diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 6b2dd0a2b4fed..1f0bf4f9887c3 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -82,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> { in_snapshot: self.in_snapshot.clone(), universe: self.universe.clone(), intercrate: self.intercrate, + next_trait_solver: self.next_trait_solver, } } } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index ed532aa2e8ba8..ccfc44a7e445a 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -109,11 +109,11 @@ impl<'tcx> InferCtxt<'tcx> { | ( ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), ty::Alias(AliasKind::Projection, _), - ) if self.tcx.trait_solver_next() => { + ) if self.next_trait_solver() => { bug!() } - (_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => { + (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { relation.register_type_relate_obligation(a, b); Ok(a) } @@ -227,9 +227,22 @@ impl<'tcx> InferCtxt<'tcx> { return self.unify_const_variable(vid, a, relation.param_env()); } (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) - if self.tcx.features().generic_const_exprs || self.tcx.trait_solver_next() => + if self.tcx.features().generic_const_exprs || self.next_trait_solver() => { - relation.register_const_equate_obligation(a, b); + let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; + + relation.register_predicates([ty::Binder::dummy( + if self.next_trait_solver() { + ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ) + } else { + ty::PredicateKind::ConstEquate(a, b) + }, + )]); + return Ok(b); } _ => {} @@ -453,19 +466,6 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { /// be used if control over the obligation causes is required. fn register_predicates(&mut self, obligations: impl IntoIterator>); - /// Register an obligation that both constants must be equal to each other. - /// - /// If they aren't equal then the relation doesn't hold. - fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - - self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() { - ty::PredicateKind::AliasRelate(a.into(), b.into(), ty::AliasRelationDirection::Equate) - } else { - ty::PredicateKind::ConstEquate(a, b) - })]); - } - /// Register an obligation that both types must be related to each other according to /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`] fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 42dfe4f6bb812..495c250a77d07 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -105,7 +105,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() - && !self.tcx().trait_solver_next() => + && !self.fields.infcx.next_trait_solver() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 7190d33d299b7..9ef35429fe32c 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -113,7 +113,7 @@ where | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if this.define_opaque_types() == DefineOpaqueTypes::Yes && def_id.is_local() - && !this.tcx().trait_solver_next() => + && !this.infcx().next_trait_solver() => { this.register_obligations( infcx diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 447d4c9f84bc0..7041e21a3812f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -330,6 +330,8 @@ pub struct InferCtxt<'tcx> { /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. pub intercrate: bool, + + next_trait_solver: bool, } /// See the `error_reporting` module for more details. @@ -545,6 +547,9 @@ pub struct InferCtxtBuilder<'tcx> { skip_leak_check: bool, /// Whether we are in coherence mode. intercrate: bool, + /// Whether we should use the new trait solver in the local inference context, + /// which affects things like which solver is used in `predicate_may_hold`. + next_trait_solver: bool, } pub trait TyCtxtInferExt<'tcx> { @@ -559,6 +564,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { considering_regions: true, skip_leak_check: false, intercrate: false, + next_trait_solver: self.next_trait_solver_globally(), } } } @@ -575,6 +581,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn with_next_trait_solver(mut self, next_trait_solver: bool) -> Self { + self.next_trait_solver = next_trait_solver; + self + } + pub fn intercrate(mut self, intercrate: bool) -> Self { self.intercrate = intercrate; self @@ -617,6 +628,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { considering_regions, skip_leak_check, intercrate, + next_trait_solver, } = *self; InferCtxt { tcx, @@ -634,6 +646,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { in_snapshot: Cell::new(false), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, + next_trait_solver, } } } @@ -670,6 +683,10 @@ pub struct CombinedSnapshot<'tcx> { } impl<'tcx> InferCtxt<'tcx> { + pub fn next_trait_solver(&self) -> bool { + self.next_trait_solver + } + /// Creates a `TypeErrCtxt` for emitting various inference errors. /// During typeck, use `FnCtxt::err_ctxt` instead. pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index d3fd01b964255..71c07f31bc952 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -491,12 +491,12 @@ where ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => { + ) if a_def_id == b_def_id || infcx.next_trait_solver() => { infcx.super_combine_tys(self, a, b).or_else(|err| { // This behavior is only there for the old solver, the new solver // shouldn't ever fail. Instead, it unconditionally emits an // alias-relate goal. - assert!(!self.tcx().trait_solver_next()); + assert!(!self.infcx.next_trait_solver()); self.tcx().sess.delay_span_bug( self.delegate.span(), "failure to relate an opaque to itself should result in an error later on", @@ -506,7 +506,7 @@ where } (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() && !self.tcx().trait_solver_next() => + if def_id.is_local() && !self.infcx.next_trait_solver() => { self.relate_opaques(a, b) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 105a3f08c8205..a9ead429f4c71 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -49,7 +49,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> InferOk<'tcx, T> { // We handle opaque types differently in the new solver. - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { return InferOk { value, obligations: vec![] }; } @@ -578,7 +578,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, ) -> InferResult<'tcx, ()> { - assert!(self.tcx.trait_solver_next()); + assert!(self.next_trait_solver()); let origin = self .opaque_type_origin(opaque_type_key.def_id) .expect("should be called for defining usages only"); @@ -614,7 +614,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::Alias(ty::Projection, projection_ty) if !projection_ty.has_escaping_bound_vars() && !tcx.is_impl_trait_in_trait(projection_ty.def_id) - && !tcx.trait_solver_next() => + && !self.next_trait_solver() => { self.infer_projection( param_env, diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index fa6529dfa93ec..4f8c9188cf85b 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -21,7 +21,7 @@ impl<'tcx> InferCtxt<'tcx> { recursion_depth: usize, obligations: &mut Vec>, ) -> Ty<'tcx> { - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { // FIXME(-Ztrait-solver=next): Instead of branching here, // completely change the normalization routine with the new solver. // diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index ceafafb5582cd..d9f9d2aabdbf7 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -132,7 +132,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() - && !self.tcx().trait_solver_next() => + && !self.fields.infcx.next_trait_solver() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index bf6f21968d71b..33ca68a0d0385 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2333,7 +2333,7 @@ impl<'tcx> TyCtxt<'tcx> { self.opt_local_def_id_to_hir_id(local_def_id).unwrap() } - pub fn trait_solver_next(self) -> bool { + pub fn next_trait_solver_globally(self) -> bool { self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index bc93b9e99ad44..3001d9f1b1f3f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -187,6 +187,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let (ref infcx, input, var_values) = tcx .infer_ctxt() .intercrate(intercrate) + .with_next_trait_solver(true) .with_opaque_type_inference(canonical_input.value.anchor) .build_with_canonical(DUMMY_SP, &canonical_input); 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 01c74be7057c9..c4481b39e14be 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1047,7 +1047,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // (which may fail). span_bug!(span, "WF predicate not satisfied for {:?}", ty); } - TraitSolver::Chalk | TraitSolver::Next => { + TraitSolver::Chalk | TraitSolver::Next | TraitSolver::NextCoherence => { // FIXME: we'll need a better message which takes into account // which bounds actually failed to hold. self.tcx.sess.struct_span_err( 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 a8a74d7501abf..f8ceee5005443 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -78,7 +78,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { self.probe(|snapshot| { let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); fulfill_cx.register_predicate_obligation(self, obligation.clone()); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 642fdec2d9ae3..9d7933e23a8b2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -146,7 +146,7 @@ where infcx: &InferCtxt<'tcx>, span: Span, ) -> Result, ErrorGuaranteed> { - if infcx.tcx.trait_solver_next() { + if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 42c1b629ac242..25e5b5e17deff 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.evaluation_probe(|this| { let goal = this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - let mut result = if this.tcx().trait_solver_next() { + let mut result = if this.infcx.next_trait_solver() { this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])? } else { this.evaluate_predicate_recursively( @@ -593,7 +593,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator> + std::fmt::Debug, { - if self.tcx().trait_solver_next() { + if self.infcx.next_trait_solver() { self.evaluate_predicates_recursively_in_new_solver(predicates) } else { let mut result = EvaluatedToOk; diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index af8dd0da5792a..84746eba3ecf8 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { ) -> Result, Vec>> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); - if self.infcx.tcx.trait_solver_next() { + if self.infcx.next_trait_solver() { while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() { let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::NormalizeProjectionType, diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index f5b2753b7973d..73756caf37272 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -16,7 +16,7 @@ fn evaluate_obligation<'tcx>( tcx: TyCtxt<'tcx>, canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result { - assert!(!tcx.trait_solver_next()); + assert!(!tcx.next_trait_solver_globally()); debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); // HACK This bubble is required for this tests to pass: // impl-trait/issue99642.rs From b637048a89654d105a17b6b6f1a060fd8dcfd093 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 31 May 2023 01:02:40 +0000 Subject: [PATCH 733/806] Add -Ztrait-solver=next-coherence --- compiler/rustc_middle/src/ty/context.rs | 8 ++++++++ compiler/rustc_session/src/config.rs | 2 ++ compiler/rustc_session/src/options.rs | 1 + compiler/rustc_trait_selection/src/traits/coherence.rs | 1 + compiler/rustc_trait_selection/src/traits/engine.rs | 2 ++ 5 files changed, 14 insertions(+) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 33ca68a0d0385..4ee543bbfb6dc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2337,6 +2337,14 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next } + pub fn next_trait_solver_in_coherence(self) -> bool { + matches!( + self.sess.opts.unstable_opts.trait_solver, + rustc_session::config::TraitSolver::Next + | rustc_session::config::TraitSolver::NextCoherence + ) + } + 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 } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0ce83e7909771..24291301a3293 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -610,6 +610,8 @@ pub enum TraitSolver { Chalk, /// Experimental trait solver in `rustc_trait_selection::solve` Next, + /// Use the new trait solver during coherence + NextCoherence, } pub enum Input { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 7cc2b2c880c60..201066e395017 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -986,6 +986,7 @@ mod parse { Some("classic") => *slot = TraitSolver::Classic, Some("chalk") => *slot = TraitSolver::Chalk, Some("next") => *slot = TraitSolver::Next, + Some("next-coherence") => *slot = TraitSolver::NextCoherence, // default trait solver is subject to change.. Some("default") => *slot = TraitSolver::Classic, _ => return false, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index d6fd457de06da..2c793ea33dc88 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -182,6 +182,7 @@ fn overlap<'tcx>( .with_opaque_type_inference(DefiningAnchor::Bubble) .skip_leak_check(skip_leak_check.is_yes()) .intercrate(true) + .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); let selcx = &mut SelectionContext::new(&infcx); if track_ambiguity_causes.is_yes() { diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 2c5ffd664fe1d..1ddff3b66d54d 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -35,6 +35,7 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box { match tcx.sess.opts.unstable_opts.trait_solver { TraitSolver::Classic => Box::new(FulfillmentContext::new()), + TraitSolver::NextCoherence => Box::new(FulfillmentContext::new()), TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()), TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), } @@ -43,6 +44,7 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box { match tcx.sess.opts.unstable_opts.trait_solver { TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()), + TraitSolver::NextCoherence => Box::new(FulfillmentContext::new_in_snapshot()), TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()), TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), } From 3d4da98273553b2307d8ce3a03c476e459aa3f45 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 31 May 2023 01:21:38 +0000 Subject: [PATCH 734/806] Make TraitEngine::new use the right solver, add compare mode --- compiler/rustc_hir_analysis/src/autoderef.rs | 3 +- .../rustc_hir_analysis/src/check/check.rs | 2 +- compiler/rustc_hir_typeck/src/inherited.rs | 2 +- compiler/rustc_infer/src/infer/combine.rs | 20 ++++---- .../src/traits/engine.rs | 48 ++++++++++++------- compiler/rustc_traits/src/codegen.rs | 2 +- src/tools/compiletest/src/common.rs | 1 + src/tools/compiletest/src/runtest.rs | 3 ++ 8 files changed, 49 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index f624118a4f1f0..6eb18aecd653f 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -161,8 +161,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, Vec>)> { - let tcx = self.infcx.tcx; - let mut fulfill_cx = >::new_in_snapshot(tcx); + let mut fulfill_cx = >::new_in_snapshot(self.infcx); let cause = traits::ObligationCause::misc(self.span, self.body_id); let normalized_ty = match self diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4a1f8ece7b285..17be5fe66cf62 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1549,7 +1549,7 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) { .with_opaque_type_inference(DefiningAnchor::Bind(def_id)) .build(); - let mut fulfillment_cx = >::new(infcx.tcx); + let mut fulfillment_cx = >::new(&infcx); for (predicate, cause) in generator_interior_predicates { let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate); fulfillment_cx.register_predicate_obligation(&infcx, obligation); diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 294c3bb78a5ba..aa4f90b4ad8a0 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -86,8 +86,8 @@ impl<'tcx> Inherited<'tcx> { Inherited { typeck_results, + fulfillment_cx: RefCell::new(>::new(&infcx)), infcx, - fulfillment_cx: RefCell::new(>::new(tcx)), locals: RefCell::new(Default::default()), deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index ccfc44a7e445a..7a80ee98cb3c1 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -231,17 +231,15 @@ impl<'tcx> InferCtxt<'tcx> { { let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; - relation.register_predicates([ty::Binder::dummy( - if self.next_trait_solver() { - ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Equate, - ) - } else { - ty::PredicateKind::ConstEquate(a, b) - }, - )]); + relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() { + ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ) + } else { + ty::PredicateKind::ConstEquate(a, b) + })]); return Ok(b); } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 1ddff3b66d54d..90699c3cadcf5 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -27,26 +27,42 @@ use rustc_session::config::TraitSolver; use rustc_span::Span; pub trait TraitEngineExt<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box; - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box; + fn new(infcx: &InferCtxt<'tcx>) -> Box; + fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new()), - TraitSolver::NextCoherence => Box::new(FulfillmentContext::new()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), + fn new(infcx: &InferCtxt<'tcx>) -> Box { + match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { + (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { + Box::new(FulfillmentContext::new()) + } + (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + Box::new(NextFulfillmentCtxt::new()) + } + (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new()), + _ => bug!( + "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", + infcx.tcx.sess.opts.unstable_opts.trait_solver, + infcx.next_trait_solver() + ), } } - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()), - TraitSolver::NextCoherence => Box::new(FulfillmentContext::new_in_snapshot()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), + fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box { + match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { + (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { + Box::new(FulfillmentContext::new_in_snapshot()) + } + (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + Box::new(NextFulfillmentCtxt::new()) + } + (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new_in_snapshot()), + _ => bug!( + "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", + infcx.tcx.sess.opts.unstable_opts.trait_solver, + infcx.next_trait_solver() + ), } } } @@ -60,11 +76,11 @@ pub struct ObligationCtxt<'a, 'tcx> { impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(>::new(infcx.tcx)) } + Self { infcx, engine: RefCell::new(>::new(infcx)) } } pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(>::new_in_snapshot(infcx.tcx)) } + Self { infcx, engine: RefCell::new(>::new_in_snapshot(infcx)) } } pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index ddba03b0b12b3..5f84acc8a0473 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -55,7 +55,7 @@ pub fn codegen_select_candidate<'tcx>( // Currently, we use a fulfillment context to completely resolve // all nested obligations. This is because they can inform the // inference of the impl's type parameters. - let mut fulfill_cx = >::new(tcx); + let mut fulfill_cx = >::new(&infcx); let impl_source = selection.map(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index f796c89873111..96fe720630ccd 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -108,6 +108,7 @@ string_enum! { Polonius => "polonius", Chalk => "chalk", NextSolver => "next-solver", + NextSolverCoherence => "next-solver-coherence", SplitDwarf => "split-dwarf", SplitDwarfSingle => "split-dwarf-single", } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 923b2e63f2ee5..6582b534488a8 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2127,6 +2127,9 @@ impl<'test> TestCx<'test> { Some(CompareMode::NextSolver) => { rustc.args(&["-Ztrait-solver=next"]); } + Some(CompareMode::NextSolverCoherence) => { + rustc.args(&["-Ztrait-solver=next-coherence"]); + } Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => { rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]); } From aabdeedc7c812388b30575a5f53292128f4f7464 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 31 May 2023 01:39:20 +0000 Subject: [PATCH 735/806] bless coherence test --- ...ion-default-items-drop-coherence.coherence.stderr | 12 ++++++++++++ ...lization-default-items-drop-coherence.next.stderr | 12 ++++++++++++ .../specialization-default-items-drop-coherence.rs | 9 +++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr create mode 100644 tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr new file mode 100644 index 0000000000000..578db0cc65e89 --- /dev/null +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `u32` + --> $DIR/specialization-default-items-drop-coherence.rs:29:1 + | +LL | impl Overlap for u32 { + | -------------------- first implementation here +... +LL | impl Overlap for ::Id { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr new file mode 100644 index 0000000000000..578db0cc65e89 --- /dev/null +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `u32` + --> $DIR/specialization-default-items-drop-coherence.rs:29:1 + | +LL | impl Overlap for u32 { + | -------------------- first implementation here +... +LL | impl Overlap for ::Id { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs index 16ad942d5ab95..44c598f19cb9b 100644 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.rs +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs @@ -1,5 +1,8 @@ -// check-pass -// known-bug: #105782 +// revisions: classic coherence next +//[next] compile-flags: -Ztrait-solver=next +//[coherence] compile-flags: -Ztrait-solver=next-coherence +//[classic] check-pass +//[classic] known-bug: #105782 // Should fail. Default items completely drop candidates instead of ambiguity, // which is unsound during coherence, since coherence requires completeness. @@ -24,6 +27,8 @@ impl Overlap for u32 { } impl Overlap for ::Id { + //[coherence]~^ ERROR conflicting implementations of trait `Overlap` for type `u32` + //[next]~^^ ERROR conflicting implementations of trait `Overlap` for type `u32` type Assoc = Box; } From 7a2cdf20e42c5b2a4d0f259eab8968cda7ae55c2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 May 2023 19:05:09 +0000 Subject: [PATCH 736/806] Move alias-relate to its own module --- .../src/solve/alias_relate.rs | 146 ++++++++++++++++++ .../rustc_trait_selection/src/solve/mod.rs | 137 +--------------- 2 files changed, 147 insertions(+), 136 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/alias_relate.rs diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs new file mode 100644 index 0000000000000..59ab3bc3a727b --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -0,0 +1,146 @@ +use super::{EvalCtxt, SolverMode}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +/// We may need to invert the alias relation direction if dealing an alias on the RHS. +#[derive(Debug)] +enum Invert { + No, + Yes, +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_alias_relate_goal( + &mut self, + goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; + if lhs.is_infer() || rhs.is_infer() { + bug!( + "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" + ); + } + + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), + + // RHS is not a projection, only way this is true is if LHS normalizes-to RHS + (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + ), + + // LHS is not a projection, only way this is true is if RHS normalizes-to LHS + (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + ), + + (Some(alias_lhs), Some(alias_rhs)) => { + debug!("both sides are aliases"); + + let mut candidates = Vec::new(); + // LHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + )); + // RHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + )); + // Relate via substs + let subst_relate_response = self + .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction); + candidates.extend(subst_relate_response); + debug!(?candidates); + + if let Some(merged) = self.try_merge_responses(&candidates) { + Ok(merged) + } else { + // When relating two aliases and we have ambiguity, we prefer + // relating the generic arguments of the aliases over normalizing + // them. This is necessary for inference during typeck. + // + // As this is incomplete, we must not do so during coherence. + match (self.solver_mode(), subst_relate_response) { + (SolverMode::Normal, Ok(response)) => Ok(response), + (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => { + self.flounder(&candidates) + } + } + } + } + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + let other = match direction { + // This is purely an optimization. + ty::AliasRelationDirection::Equate => other, + + ty::AliasRelationDirection::Subtype => { + let fresh = ecx.next_term_infer_of_kind(other); + let (sub, sup) = match invert { + Invert::No => (fresh, other), + Invert::Yes => (other, fresh), + }; + ecx.sub(param_env, sub, sup)?; + fresh + } + }; + ecx.add_goal(Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn assemble_subst_relate_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias_lhs: ty::AliasTy<'tcx>, + alias_rhs: ty::AliasTy<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + match direction { + ty::AliasRelationDirection::Equate => { + ecx.eq(param_env, alias_lhs, alias_rhs)?; + } + ty::AliasRelationDirection::Subtype => { + ecx.sub(param_env, alias_lhs, alias_rhs)?; + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 56a254d9c07e1..f4c29c837b884 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; +mod alias_relate; mod assembly; mod canonicalize; mod eval_ctxt; @@ -154,142 +155,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self), ret)] - fn compute_alias_relate_goal( - &mut self, - goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - // We may need to invert the alias relation direction if dealing an alias on the RHS. - #[derive(Debug)] - enum Invert { - No, - Yes, - } - let evaluate_normalizes_to = - |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(evaluate_normalizes_to)", - ?alias, - ?other, - ?direction, - ?invert - ); - let _enter = span.enter(); - let result = ecx.probe(|ecx| { - let other = match direction { - // This is purely an optimization. - ty::AliasRelationDirection::Equate => other, - - ty::AliasRelationDirection::Subtype => { - let fresh = ecx.next_term_infer_of_kind(other); - let (sub, sup) = match invert { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - ecx.sub(goal.param_env, sub, sup)?; - fresh - } - }; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: other, - }), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - debug!(?result); - result - }; - - let (lhs, rhs, direction) = goal.predicate; - - if lhs.is_infer() || rhs.is_infer() { - bug!( - "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" - ); - } - - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - - // RHS is not a projection, only way this is true is if LHS normalizes-to RHS - (Some(alias_lhs), None) => { - evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No) - } - - // LHS is not a projection, only way this is true is if RHS normalizes-to LHS - (None, Some(alias_rhs)) => { - evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes) - } - - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("both sides are aliases"); - - let mut candidates = Vec::new(); - // LHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_lhs, - rhs, - direction, - Invert::No, - )); - // RHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_rhs, - lhs, - direction, - Invert::Yes, - )); - // Relate via substs - let subst_relate_response = self.probe(|ecx| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(relate_via_substs)", - ?alias_lhs, - ?alias_rhs, - ?direction - ); - let _enter = span.enter(); - - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; - } - } - - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - candidates.extend(subst_relate_response); - debug!(?candidates); - - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) - } else { - // When relating two aliases and we have ambiguity, we prefer - // relating the generic arguments of the aliases over normalizing - // them. This is necessary for inference during typeck. - // - // As this is incomplete, we must not do so during coherence. - match (self.solver_mode(), subst_relate_response) { - (SolverMode::Normal, Ok(response)) => Ok(response), - (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => { - self.flounder(&candidates) - } - } - } - } - } - } - #[instrument(level = "debug", skip(self), ret)] fn compute_const_arg_has_type_goal( &mut self, From 3ea7c512bd1587006a1c196df841e9b7ec60fb0b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 May 2023 19:27:59 +0000 Subject: [PATCH 737/806] Fall back to bidirectional normalizes-to if no subst-eq in alias-eq goal --- .../src/solve/alias_relate.rs | 95 ++++++++++++++----- tests/ui/traits/new-solver/tait-eq-proj-2.rs | 21 ++++ tests/ui/traits/new-solver/tait-eq-proj.rs | 35 +++++++ tests/ui/traits/new-solver/tait-eq-tait.rs | 17 ++++ 4 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 tests/ui/traits/new-solver/tait-eq-proj-2.rs create mode 100644 tests/ui/traits/new-solver/tait-eq-proj.rs create mode 100644 tests/ui/traits/new-solver/tait-eq-tait.rs diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 59ab3bc3a727b..66a4d36a1e5a7 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -79,11 +79,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // them. This is necessary for inference during typeck. // // As this is incomplete, we must not do so during coherence. - match (self.solver_mode(), subst_relate_response) { - (SolverMode::Normal, Ok(response)) => Ok(response), - (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => { - self.flounder(&candidates) + match self.solver_mode() { + SolverMode::Normal => { + if let Ok(subst_relate_response) = subst_relate_response { + Ok(subst_relate_response) + } else if let Ok(bidirectional_normalizes_to_response) = self + .assemble_bidirectional_normalizes_to_candidate( + param_env, lhs, rhs, direction, + ) + { + Ok(bidirectional_normalizes_to_response) + } else { + self.flounder(&candidates) + } } + SolverMode::Coherence => self.flounder(&candidates), } } } @@ -100,29 +110,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { invert: Invert, ) -> QueryResult<'tcx> { self.probe(|ecx| { - let other = match direction { - // This is purely an optimization. - ty::AliasRelationDirection::Equate => other, - - ty::AliasRelationDirection::Subtype => { - let fresh = ecx.next_term_infer_of_kind(other); - let (sub, sup) = match invert { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - ecx.sub(param_env, sub, sup)?; - fresh - } - }; - ecx.add_goal(Goal::new( - ecx.tcx(), - param_env, - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), - )); + ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } + fn normalizes_to_inner( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> Result<(), NoSolution> { + let other = match direction { + // This is purely an optimization. + ty::AliasRelationDirection::Equate => other, + + ty::AliasRelationDirection::Subtype => { + let fresh = self.next_term_infer_of_kind(other); + let (sub, sup) = match invert { + Invert::No => (fresh, other), + Invert::Yes => (other, fresh), + }; + self.sub(param_env, sub, sup)?; + fresh + } + }; + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + )); + + Ok(()) + } + fn assemble_subst_relate_candidate( &mut self, param_env: ty::ParamEnv<'tcx>, @@ -143,4 +166,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } + + fn assemble_bidirectional_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: ty::Term<'tcx>, + rhs: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + ecx.normalizes_to_inner( + param_env, + lhs.to_alias_ty(ecx.tcx()).unwrap(), + rhs, + direction, + Invert::No, + )?; + ecx.normalizes_to_inner( + param_env, + rhs.to_alias_ty(ecx.tcx()).unwrap(), + lhs, + direction, + Invert::Yes, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } } diff --git a/tests/ui/traits/new-solver/tait-eq-proj-2.rs b/tests/ui/traits/new-solver/tait-eq-proj-2.rs new file mode 100644 index 0000000000000..99a3d02bd1aea --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-proj-2.rs @@ -0,0 +1,21 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(type_alias_impl_trait)] + +// Similar to tests/ui/traits/new-solver/tait-eq-proj.rs +// but check the alias-sub relation in the other direction. + +type Tait = impl Iterator; + +fn mk() -> T { todo!() } + +fn a() { + let x: Tait = mk(); + let mut array = mk(); + let mut z = IntoIterator::into_iter(array); + z = x; + array = [0i32; 32]; +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/tait-eq-proj.rs b/tests/ui/traits/new-solver/tait-eq-proj.rs new file mode 100644 index 0000000000000..01141b2819a8d --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-proj.rs @@ -0,0 +1,35 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(type_alias_impl_trait)] + +type Tait = impl Iterator; + +/* + +Consider the goal - AliasRelate(Tait, <[i32; 32] as IntoIterator>::IntoIter) +which is registered on the line above. + +A. SubstRelate - fails (of course). + +B. NormalizesToRhs - Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter + * infer definition - Tait := <[i32; 32] as IntoIterator>::IntoIter + +C. NormalizesToLhs - <[i32; 32] as IntoIterator>::IntoIter normalizes-to Tait + * Find impl candidate, after substitute - std::array::IntoIter + * Equate std::array::IntoIter and Tait + * infer definition - Tait := std::array::IntoIter + +B and C are not equal, but they are equivalent modulo normalization. + +We get around this by evaluating both the NormalizesToRhs and NormalizesToLhs +goals together. Essentially: + A alias-relate B if A normalizes-to B and B normalizes-to A. + +*/ + +fn a() { + let _: Tait = IntoIterator::into_iter([0i32; 32]); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/tait-eq-tait.rs b/tests/ui/traits/new-solver/tait-eq-tait.rs new file mode 100644 index 0000000000000..532c4c39bd499 --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-tait.rs @@ -0,0 +1,17 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// Not exactly sure if this is the inference behavior we *want*, +// but it is a side-effect of the lazy normalization of TAITs. + +#![feature(type_alias_impl_trait)] + +type Tait = impl Sized; +type Tait2 = impl Sized; + +fn mk() -> T { todo!() } + +fn main() { + let x: Tait = 1u32; + let y: Tait2 = x; +} From 0e01088f077c572bbca791fbed54c1bbe80afdb0 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 6 Jun 2023 18:50:38 +0000 Subject: [PATCH 738/806] Fix comment for `get_region_var_origins` --- compiler/rustc_infer/src/infer/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 447d4c9f84bc0..f36102fe2ea8c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1195,15 +1195,15 @@ impl<'tcx> InferCtxt<'tcx> { .var_origin(vid) } - /// Takes ownership of the list of variable regions. This implies - /// that all the region constraints have already been taken, and - /// hence that `resolve_regions_and_report_errors` can never be - /// called. This is used only during NLL processing to "hand off" ownership - /// of the set of region variables into the NLL region context. + /// Clone the list of variable regions. This is used only during NLL processing + /// to put the set of region variables into the NLL region context. pub fn get_region_var_origins(&self) -> VarInfos { let mut inner = self.inner.borrow_mut(); let (var_infos, data) = inner .region_constraint_storage + // We clone instead of taking because borrowck still wants to use + // the inference context after calling this for diagnostics + // and the new trait solver. .clone() .expect("regions already resolved") .with_log(&mut inner.undo_log) From 5593e7e2ba2c04c6ded2d01859d5f762bb4e043d Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:03:29 +0000 Subject: [PATCH 739/806] Avoid duplicate type sanitization of local decls The type of the local decl is already sanitized in `visit_local_decl`. --- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 908ff3da5ca0b..31e20d7e70993 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -477,9 +477,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { fn visit_body(&mut self, body: &Body<'tcx>) { self.sanitize_type(&"return type", body.return_ty()); - for local_decl in &body.local_decls { - self.sanitize_type(local_decl, local_decl.ty); - } + // The types of local_decls are checked above which is called in super_body. self.super_body(body); } } From 459bd2cbde9544be65371b7bf657aa7d2015c3fb Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:00:47 +0000 Subject: [PATCH 740/806] Remove default visitor impl in region constraint generation --- compiler/rustc_borrowck/src/constraint_generation.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index 2aa09a3f26c17..743d117e00f2b 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::visit::TyContext; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, - SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection, + Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement, + StatementKind, Terminator, TerminatorKind, UserTypeProjection, }; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::visit::TypeVisitable; @@ -49,10 +49,6 @@ struct ConstraintGeneration<'cg, 'tcx> { } impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> { - fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { - self.super_basic_block_data(bb, data); - } - /// We sometimes have `substs` within an rvalue, or within a /// call. Make them live at the location where they appear. fn visit_substs(&mut self, substs: &SubstsRef<'tcx>, location: Location) { From 0f1aaef7e9a1776a81819ef4ae05b508fb12d572 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 3 Jun 2023 10:40:46 -0700 Subject: [PATCH 741/806] rustdoc: convert `if let Some()` that always matches to variable --- src/librustdoc/visit_ast.rs | 46 ++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index db35355289349..22c8cc092438c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -14,7 +14,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::{iter, mem}; +use std::mem; use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt}; use crate::core; @@ -291,27 +291,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if !please_inline { let inherits_hidden = inherits_doc_hidden(tcx, res_did, None); // Only inline if requested or if the item would otherwise be stripped. - // - // If it's a doc hidden module, we need to keep it in case some of its inner items - // are re-exported. if (!is_private && !inherits_hidden) || ( is_hidden && + // If it's a doc hidden module, we need to keep it in case some of its inner items + // are re-exported. !matches!(item, Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_), .. })) - ) { - return false; - } else if let Some(item_def_id) = reexport_chain(tcx, def_id, res_did).iter() - .flat_map(|reexport| reexport.id()).map(|id| id.expect_local()) - .chain(iter::once(res_did)).nth(1) && - item_def_id != def_id && - self - .cx - .cache - .effective_visibilities - .is_directly_public(tcx, item_def_id.to_def_id()) && - !tcx.is_doc_hidden(item_def_id) && - !inherits_doc_hidden(tcx, item_def_id, None) - { + ) || // The imported item is public and not `doc(hidden)` so no need to inline it. + self.reexport_public_and_not_hidden(def_id, res_did) + { return false; } } @@ -359,6 +347,28 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ret } + /// Returns `true` if the item is visible, meaning it's not `#[doc(hidden)]` or private. + /// + /// This function takes into account the entire re-export `use` chain, so it needs the + /// ID of the "leaf" `use` and the ID of the "root" item. + fn reexport_public_and_not_hidden( + &self, + import_def_id: LocalDefId, + target_def_id: LocalDefId, + ) -> bool { + let tcx = self.cx.tcx; + let item_def_id = reexport_chain(tcx, import_def_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + .map(|id| id.expect_local()) + .nth(1) + .unwrap_or(target_def_id); + item_def_id != import_def_id + && self.cx.cache.effective_visibilities.is_directly_public(tcx, item_def_id.to_def_id()) + && !tcx.is_doc_hidden(item_def_id) + && !inherits_doc_hidden(tcx, item_def_id, None) + } + #[inline] fn add_to_current_mod( &mut self, From 70980929b480d6c13e23d79ad8de2ff0afbce60c Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:24:33 +0000 Subject: [PATCH 742/806] Respect `RUST_BACKTRACE` for delayed bugs Sometimes, especially with MIR validation, the backtraces from delayed bugs are noise and make it harder to look at them. Respect the environment variable and don't print it when the user doesn't want it. --- compiler/rustc_errors/src/lib.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index bf77ed81f9bd0..7a297ea0d5f82 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -383,7 +383,7 @@ pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; pub use diagnostic_impls::{ DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans, }; -use std::backtrace::Backtrace; +use std::backtrace::{Backtrace, BacktraceStatus}; /// A handler deals with errors and other compiler output. /// Certain errors (fatal, bug, unimpl) may cause immediate exit, @@ -1331,7 +1331,7 @@ impl HandlerInner { // once *any* errors were emitted (and truncate `delayed_span_bugs` // when an error is first emitted, also), but maybe there's a case // in which that's not sound? otherwise this is really inefficient. - let backtrace = std::backtrace::Backtrace::force_capture(); + let backtrace = std::backtrace::Backtrace::capture(); self.delayed_span_bugs .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); @@ -1620,7 +1620,7 @@ impl HandlerInner { if self.flags.report_delayed_bugs { self.emit_diagnostic(&mut diagnostic); } - let backtrace = std::backtrace::Backtrace::force_capture(); + let backtrace = std::backtrace::Backtrace::capture(); self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } @@ -1739,7 +1739,17 @@ impl DelayedDiagnostic { } fn decorate(mut self) -> Diagnostic { - self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + match self.note.status() { + BacktraceStatus::Captured => { + self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + } + // Avoid the needless newline when no backtrace has been captured, + // the display impl should just be a single line. + _ => { + self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note)); + } + } + self.inner } } From 9b1a1e1d95d1e40bdf57ef9d37ccbac91fc9c280 Mon Sep 17 00:00:00 2001 From: Jing Peng Date: Sun, 26 Feb 2023 15:27:27 -0500 Subject: [PATCH 743/806] Write to stdout if `-` is given as output file If `-o -` or `--emit KIND=-` is provided, output will be written to stdout instead. Binary output (`obj`, `llvm-bc`, `link` and `metadata`) being written this way will result in an error unless stdout is not a tty. Multiple output types going to stdout will trigger an error too, as they will all be mixded together. --- Cargo.lock | 2 + compiler/rustc_codegen_ssa/messages.ftl | 2 + compiler/rustc_codegen_ssa/src/back/link.rs | 24 +++- compiler/rustc_codegen_ssa/src/back/write.rs | 23 +++- compiler/rustc_codegen_ssa/src/errors.rs | 6 + compiler/rustc_driver_impl/src/lib.rs | 13 ++- compiler/rustc_driver_impl/src/pretty.rs | 6 +- compiler/rustc_interface/Cargo.toml | 1 + compiler/rustc_interface/messages.ftl | 1 + compiler/rustc_interface/src/errors.rs | 4 + compiler/rustc_interface/src/interface.rs | 4 +- compiler/rustc_interface/src/passes.rs | 75 ++++++++----- compiler/rustc_interface/src/tests.rs | 20 ++-- compiler/rustc_interface/src/util.rs | 33 +++++- compiler/rustc_metadata/messages.ftl | 9 ++ compiler/rustc_metadata/src/errors.rs | 18 +++ compiler/rustc_metadata/src/fs.rs | 50 +++++++-- compiler/rustc_mir_transform/src/dump_mir.rs | 15 ++- compiler/rustc_session/Cargo.toml | 1 + compiler/rustc_session/src/config.rs | 105 ++++++++++++++++-- compiler/rustc_session/src/output.rs | 43 ++++--- compiler/rustc_session/src/session.rs | 6 +- src/doc/rustc/src/command-line-arguments.md | 6 + .../hotplug_codegen_backend/the_backend.rs | 14 ++- tests/run-make-fulldeps/issue-19371/foo.rs | 4 +- tests/run-make/emit-to-stdout/Makefile | 51 +++++++++ .../run-make/emit-to-stdout/emit-link.stderr | 4 + .../emit-to-stdout/emit-llvm-bc.stderr | 4 + .../emit-to-stdout/emit-metadata.stderr | 4 + .../emit-to-stdout/emit-multiple-types.stderr | 4 + tests/run-make/emit-to-stdout/emit-obj.stderr | 4 + tests/run-make/emit-to-stdout/test.rs | 1 + 32 files changed, 456 insertions(+), 101 deletions(-) create mode 100644 tests/run-make/emit-to-stdout/Makefile create mode 100644 tests/run-make/emit-to-stdout/emit-link.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-llvm-bc.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-metadata.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-multiple-types.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-obj.stderr create mode 100644 tests/run-make/emit-to-stdout/test.rs diff --git a/Cargo.lock b/Cargo.lock index 0369442f11cf6..650553e0d8343 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3702,6 +3702,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "atty", "libloading", "rustc-rayon", "rustc-rayon-core", @@ -4166,6 +4167,7 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ + "atty", "getopts", "libc", "rustc_ast", diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 9aa2b2e2b2ed0..5ecb63986fe1f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -9,6 +9,8 @@ codegen_ssa_archive_build_failure = codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering +codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty + codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8a00c42a0e8bd..311e56cc7d15e 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; -use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; +use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -68,6 +68,7 @@ pub fn link_binary<'a>( ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + let mut tempfiles_for_stdout_output: Vec = Vec::new(); for &crate_type in sess.crate_types().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -97,12 +98,15 @@ pub fn link_binary<'a>( .tempdir() .unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); - let out_filename = out_filename( + let output = out_filename( sess, crate_type, outputs, codegen_results.crate_info.local_crate_name, ); + let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); + let out_filename = + output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str())); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); @@ -152,6 +156,17 @@ pub fn link_binary<'a>( ); } } + + if output.is_stdout() { + if output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { + shorthand: OutputType::Exe.shorthand(), + }); + } else if let Err(e) = copy_to_stdout(&out_filename) { + sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e)); + } + tempfiles_for_stdout_output.push(out_filename); + } } } @@ -189,6 +204,11 @@ pub fn link_binary<'a>( remove_temps_from_module(allocator_module); } + // Remove the temporary files if output goes to stdout + for temp in tempfiles_for_stdout_output { + ensure_removed(sess.diagnostic(), &temp); + } + // If no requested outputs require linking, then the object temporaries should // be kept. if !sess.opts.output_types.should_link() { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c323372bda42d..a1e8725e08edc 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -23,12 +23,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_metadata::fs::copy_to_stdout; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; -use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType}; use rustc_session::config::{Passes, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::source_map::SourceMap; @@ -535,9 +536,16 @@ fn produce_final_output_artifacts( let mut user_wants_objects = false; // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.emit_err(errors::CopyPath::new(from, to, e)); + let copy_gracefully = |from: &Path, to: &OutFileName| match to { + OutFileName::Stdout => { + if let Err(e) = copy_to_stdout(from) { + sess.emit_err(errors::CopyPath::new(from, to.as_path(), e)); + } + } + OutFileName::Real(path) => { + if let Err(e) = fs::copy(from, path) { + sess.emit_err(errors::CopyPath::new(from, path, e)); + } } }; @@ -547,7 +555,12 @@ fn produce_final_output_artifacts( // to copy `foo.0.x` to `foo.x`. let module_name = Some(&compiled_modules.modules[0].name[..]); let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, &crate_output.path(output_type)); + let output = crate_output.path(output_type); + if !output_type.is_text_output() && output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); + } else { + copy_gracefully(&path, &output); + } if !sess.opts.cg.save_temps && !keep_numbered { // The user just wants `foo.x`, not `foo.#module-name#.x`. ensure_removed(sess.diagnostic(), &path); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cf4893b822651..3fed9ea0b41e0 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -82,6 +82,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> { } } +#[derive(Diagnostic)] +#[diag(codegen_ssa_binary_output_to_tty)] +pub struct BinaryOutputToTty { + pub shorthand: &'static str, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_emit_path)] pub struct IgnoringEmitPath { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 40aa69e5a4105..416603a0e1ca2 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -34,7 +34,9 @@ use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; +use rustc_session::config::{ + ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths, +}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -437,9 +439,12 @@ fn run_compiler( } // Extract output directory and file from matches. -fn make_output(matches: &getopts::Matches) -> (Option, Option) { +fn make_output(matches: &getopts::Matches) -> (Option, Option) { let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o)); - let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o)); + let ofile = matches.opt_str("o").map(|o| match o.as_str() { + "-" => OutFileName::Stdout, + path => OutFileName::Real(PathBuf::from(path)), + }); (odir, ofile) } @@ -685,7 +690,7 @@ fn print_crate_info( for &style in &crate_types { let fname = rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.file_name().unwrap().to_string_lossy()); + safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } Cfg => { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index ee64b18d3f60c..24a5f4030b88d 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -9,7 +9,7 @@ use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::map as hir_map; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; +use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::FileName; @@ -359,8 +359,8 @@ fn get_source(sess: &Session) -> (String, FileName) { fn write_or_print(out: &str, sess: &Session) { match &sess.io.output_file { - None => print!("{out}"), - Some(p) => { + None | Some(OutFileName::Stdout) => print!("{out}"), + Some(OutFileName::Real(p)) => { if let Err(e) = std::fs::write(p, out) { sess.emit_fatal(UnprettyDumpFail { path: p.display().to_string(), diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 2c7438ed9db43..7826d42dcb2d7 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +atty = "0.2.13" libloading = "0.7.1" tracing = "0.1" rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index be1a75f020b4d..bd9fad8b04296 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -33,6 +33,7 @@ interface_mixed_proc_macro_crate = interface_multiple_output_types_adaption = due to multiple output types requested, the explicitly specified output file name will be adapted for each output type +interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout interface_out_dir_error = failed to find or create the directory specified by `--out-dir` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 0eedee250269e..a9ab2720d89a5 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -108,3 +108,7 @@ pub struct IgnoringExtraFilename; #[derive(Diagnostic)] #[diag(interface_ignoring_out_dir)] pub struct IgnoringOutDir; + +#[derive(Diagnostic)] +#[diag(interface_multiple_output_types_to_stdout)] +pub struct MultipleOutputTypesToStdout; diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 39d56897999d9..2edc72ba72ea1 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -14,7 +14,7 @@ use rustc_middle::{bug, ty}; use rustc_parse::maybe_new_parser_from_source_str; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; -use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; +use rustc_session::config::{self, ErrorOutputType, Input, OutFileName, OutputFilenames}; use rustc_session::config::{CheckCfg, ExpectedValues}; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; @@ -252,7 +252,7 @@ pub struct Config { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub file_loader: Option>, pub locale_resources: &'static [&'static str], diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 42d8d22809178..83a74742f5b76 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -24,7 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::Resolver; -use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; +use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::{MetadataLoader, Untracked}; use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; @@ -373,19 +373,23 @@ fn generated_output_paths( ) -> Vec { let mut out_filenames = Vec::new(); for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); + let out_filename = outputs.path(*output_type); + let file = out_filename.as_path().to_path_buf(); match *output_type { // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => { for crate_type in sess.crate_types().iter() { let p = filename_for_input(sess, *crate_type, crate_name, outputs); - out_filenames.push(p); + out_filenames.push(p.as_path().to_path_buf()); } } OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => { // Don't add the dep-info output when omitting it from dep-info targets } + OutputType::DepInfo if out_filename.is_stdout() => { + // Don't add the dep-info output when it goes to stdout + } _ => { out_filenames.push(file); } @@ -452,7 +456,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { return; } - let deps_filename = outputs.path(OutputType::DepInfo); + let deps_output = outputs.path(OutputType::DepInfo); + let deps_filename = deps_output.as_path(); let result: io::Result<()> = try { // Build a list of files used to compile the output and @@ -515,33 +520,47 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } - let mut file = BufWriter::new(fs::File::create(&deps_filename)?); - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } + let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{path}:")?; - } + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{path}:")?; + } - // Emit special comments with information about accessed environment variables. - let env_depinfo = sess.parse_sess.env_depinfo.borrow(); - if !env_depinfo.is_empty() { - let mut envs: Vec<_> = env_depinfo - .iter() - .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) - .collect(); - envs.sort_unstable(); - writeln!(file)?; - for (k, v) in envs { - write!(file, "# env-dep:{k}")?; - if let Some(v) = v { - write!(file, "={v}")?; - } + // Emit special comments with information about accessed environment variables. + let env_depinfo = sess.parse_sess.env_depinfo.borrow(); + if !env_depinfo.is_empty() { + let mut envs: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) + .collect(); + envs.sort_unstable(); writeln!(file)?; + for (k, v) in envs { + write!(file, "# env-dep:{k}")?; + if let Some(v) = v { + write!(file, "={v}")?; + } + writeln!(file)?; + } + } + + Ok(()) + }; + + match deps_output { + OutFileName::Stdout => { + let mut file = BufWriter::new(io::stdout()); + write_deps_to_file(&mut file)?; + } + OutFileName::Real(ref path) => { + let mut file = BufWriter::new(fs::File::create(path)?); + write_deps_to_file(&mut file)?; } } }; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 28e719a40e565..77ee2b40e37c1 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -11,7 +11,7 @@ use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ - BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet, + BranchProtection, Externs, OomStrategy, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel, }; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; @@ -167,8 +167,14 @@ fn test_output_types_tracking_hash_different_paths() { let mut v2 = Options::default(); let mut v3 = Options::default(); - v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); - v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); + v1.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("./some/thing"))), + )]); + v2.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("/some/thing"))), + )]); v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); assert_non_crate_hash_different(&v1, &v2); @@ -182,13 +188,13 @@ fn test_output_types_tracking_hash_different_construction_order() { let mut v2 = Options::default(); v1.output_types = OutputTypes::new(&[ - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), ]); v2.output_types = OutputTypes::new(&[ - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), ]); assert_same_hash(&v1, &v2); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index cb19750203e85..87252fefb1e2c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,7 +11,7 @@ use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::CheckCfg; use rustc_session::config::{self, CrateType}; -use rustc_session::config::{ErrorOutputType, OutputFilenames}; +use rustc_session::config::{ErrorOutputType, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::filesearch::sysroot_candidates; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; @@ -500,7 +500,36 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec bool { + if atty::is(atty::Stream::Stdout) { + // If stdout is a tty, check if multiple text output types are + // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar` + let named_text_types = output_types + .iter() + .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout)) + .count(); + let unnamed_text_types = + output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count(); + named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout + } else { + // Otherwise, all the output types should be checked + let named_types = + output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count(); + let unnamed_types = output_types.values().filter(|o| o.is_none()).count(); + named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout + } +} + pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames { + if multiple_output_types_to_stdout( + &sess.opts.output_types, + sess.io.output_file == Some(OutFileName::Stdout), + ) { + sess.emit_fatal(errors::MultipleOutputTypesToStdout); + } match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -544,7 +573,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu OutputFilenames::new( out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), + out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(), ofile, sess.io.temps_dir.clone(), sess.opts.cg.extra_filename.clone(), diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 6d8601b9e2bc5..d6b08d840e38b 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -4,6 +4,9 @@ metadata_as_needed_compatibility = metadata_bad_panic_strategy = the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` +metadata_binary_output_to_tty = + option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + metadata_bundle_needs_static = linking modifier `bundle` is only compatible with `static` linking kind @@ -63,6 +66,9 @@ metadata_fail_seek_file = metadata_fail_write_file = failed to write to the file: {$err} +metadata_failed_copy_to_stdout = + failed to copy {$filename} to stdout: {$err} + metadata_failed_create_encoded_metadata = failed to create encoded metadata from file: {$err} @@ -72,6 +78,9 @@ metadata_failed_create_file = metadata_failed_create_tempdir = couldn't create a temp dir: {$err} +metadata_failed_remove = + failed to remove {$filename}: {$err} + metadata_failed_write_error = failed to write {$filename}: {$err} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index a44c1dd582ed3..e110c68321d46 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -395,6 +395,24 @@ pub struct FailedWriteError { pub err: Error, } +#[derive(Diagnostic)] +#[diag(metadata_failed_copy_to_stdout)] +pub struct FailedCopyToStdout { + pub filename: PathBuf, + pub err: Error, +} + +#[derive(Diagnostic)] +#[diag(metadata_failed_remove)] +pub struct FailedRemove { + pub filename: PathBuf, + pub err: Error, +} + +#[derive(Diagnostic)] +#[diag(metadata_binary_output_to_tty)] +pub struct BinaryOutputToTty; + #[derive(Diagnostic)] #[diag(metadata_missing_native_library)] pub struct MissingNativeLibrary<'a> { diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 08de828fbdba2..5be99c8e4c0d8 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,18 +1,19 @@ use crate::errors::{ - FailedCreateEncodedMetadata, FailedCreateFile, FailedCreateTempdir, FailedWriteError, + BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, + FailedCreateTempdir, FailedRemove, FailedWriteError, }; 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::OutputType; +use rustc_session::config::{OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; use tempfile::Builder as TempFileBuilder; -use std::fs; use std::path::{Path, PathBuf}; +use std::{fs, io}; // FIXME(eddyb) maybe include the crate name in this? pub const METADATA_FILENAME: &str = "lib.rmeta"; @@ -74,26 +75,47 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // this file always exists. let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); let (metadata_filename, metadata_tmpdir) = if need_metadata_file { - if let Err(err) = non_durable_rename(&metadata_filename, &out_filename) { - tcx.sess.emit_fatal(FailedWriteError { filename: out_filename, err }); - } + let filename = match out_filename { + OutFileName::Real(ref path) => { + if let Err(err) = non_durable_rename(&metadata_filename, path) { + tcx.sess.emit_fatal(FailedWriteError { filename: path.to_path_buf(), err }); + } + path.clone() + } + OutFileName::Stdout => { + if out_filename.is_tty() { + tcx.sess.emit_err(BinaryOutputToTty); + } else if let Err(err) = copy_to_stdout(&metadata_filename) { + tcx.sess + .emit_err(FailedCopyToStdout { filename: metadata_filename.clone(), err }); + } + metadata_filename + } + }; if tcx.sess.opts.json_artifact_notifications { tcx.sess .parse_sess .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); + .emit_artifact_notification(&out_filename.as_path(), "metadata"); } - (out_filename, None) + (filename, None) } else { (metadata_filename, Some(metadata_tmpdir)) }; // Load metadata back to memory: codegen may need to include it in object files. - let metadata = - EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { + let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir) + .unwrap_or_else(|err| { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); + // Delete the temporary metadata file if output is stdout + if need_metadata_file && out_filename.is_stdout() { + if let Err(err) = fs::remove_file(&metadata_filename) { + tcx.sess.emit_err(FailedRemove { filename: metadata_filename, err }); + } + } + let need_metadata_module = metadata_kind == MetadataKind::Compressed; (metadata, need_metadata_module) @@ -116,3 +138,11 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { let _ = std::fs::remove_file(dst); std::fs::rename(src, dst) } + +pub fn copy_to_stdout(from: &Path) -> io::Result<()> { + let file = fs::File::open(from)?; + let mut reader = io::BufReader::new(file); + let mut stdout = io::stdout(); + io::copy(&mut reader, &mut stdout)?; + Ok(()) +} diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 746e3d9652db6..13841be494cf0 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -7,7 +7,7 @@ use crate::MirPass; use rustc_middle::mir::write_mir_pretty; use rustc_middle::mir::Body; use rustc_middle::ty::TyCtxt; -use rustc_session::config::OutputType; +use rustc_session::config::{OutFileName, OutputType}; pub struct Marker(pub &'static str); @@ -20,8 +20,15 @@ impl<'tcx> MirPass<'tcx> for Marker { } pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { - let path = tcx.output_filenames(()).path(OutputType::Mir); - let mut f = io::BufWriter::new(File::create(&path)?); - write_mir_pretty(tcx, None, &mut f)?; + match tcx.output_filenames(()).path(OutputType::Mir) { + OutFileName::Stdout => { + let mut f = io::stdout(); + write_mir_pretty(tcx, None, &mut f)?; + } + OutFileName::Real(path) => { + let mut f = io::BufWriter::new(File::create(&path)?); + write_mir_pretty(tcx, None, &mut f)?; + } + } Ok(()) } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 3af83aaaaa8a2..90ad3f90f2c31 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] +atty = "0.2.13" getopts = "0.2" rustc_macros = { path = "../rustc_macros" } tracing = "0.1" diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 6c8c8e484f939..b72a95639e65c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -30,6 +30,7 @@ use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, }; use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::OsStr; use std::fmt; use std::hash::Hash; use std::iter; @@ -333,7 +334,7 @@ impl OutputType { } } - fn shorthand(&self) -> &'static str { + pub fn shorthand(&self) -> &'static str { match *self { OutputType::Bitcode => "llvm-bc", OutputType::Assembly => "asm", @@ -386,6 +387,18 @@ impl OutputType { OutputType::Exe => "", } } + + pub fn is_text_output(&self) -> bool { + match *self { + OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::DepInfo => true, + OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => { + false + } + } + } } /// The type of diagnostics output to generate. @@ -438,14 +451,14 @@ pub enum ResolveDocLinks { /// dependency tracking for command-line arguments. Also only hash keys, since tracking /// should only depend on the output types, not the paths they're written to. #[derive(Clone, Debug, Hash, HashStable_Generic)] -pub struct OutputTypes(BTreeMap>); +pub struct OutputTypes(BTreeMap>); impl OutputTypes { - pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) } - pub fn get(&self, key: &OutputType) -> Option<&Option> { + pub fn get(&self, key: &OutputType) -> Option<&Option> { self.0.get(key) } @@ -453,11 +466,15 @@ impl OutputTypes { self.0.contains_key(key) } - pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { + pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option> { + self.0.iter() + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { self.0.keys() } - pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { self.0.values() } @@ -658,11 +675,71 @@ impl Input { } } +#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq)] +pub enum OutFileName { + Real(PathBuf), + Stdout, +} + +impl OutFileName { + pub fn parent(&self) -> Option<&Path> { + match *self { + OutFileName::Real(ref path) => path.parent(), + OutFileName::Stdout => None, + } + } + + pub fn filestem(&self) -> Option<&OsStr> { + match *self { + OutFileName::Real(ref path) => path.file_stem(), + OutFileName::Stdout => Some(OsStr::new("stdout")), + } + } + + pub fn is_stdout(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => true, + } + } + + pub fn is_tty(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => atty::is(atty::Stream::Stdout), + } + } + + pub fn as_path(&self) -> &Path { + match *self { + OutFileName::Real(ref path) => path.as_ref(), + OutFileName::Stdout => &Path::new("stdout"), + } + } + + /// For a given output filename, return the actual name of the file that + /// can be used to write codegen data of type `flavor`. For real-path + /// output filenames, this would be trivial as we can just use the path. + /// Otherwise for stdout, return a temporary path so that the codegen data + /// may be later copied to stdout. + pub fn file_for_writing( + &self, + outputs: &OutputFilenames, + flavor: OutputType, + codegen_unit_name: Option<&str>, + ) -> PathBuf { + match *self { + OutFileName::Real(ref path) => path.clone(), + OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), + } + } +} + #[derive(Clone, Hash, Debug, HashStable_Generic)] pub struct OutputFilenames { pub out_directory: PathBuf, filestem: String, - pub single_output_file: Option, + pub single_output_file: Option, pub temps_directory: Option, pub outputs: OutputTypes, } @@ -675,7 +752,7 @@ impl OutputFilenames { pub fn new( out_directory: PathBuf, out_filestem: String, - single_output_file: Option, + single_output_file: Option, temps_directory: Option, extra: String, outputs: OutputTypes, @@ -689,12 +766,12 @@ impl OutputFilenames { } } - pub fn path(&self, flavor: OutputType) -> PathBuf { + pub fn path(&self, flavor: OutputType) -> OutFileName { self.outputs .get(&flavor) .and_then(|p| p.to_owned()) .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.output_path(flavor)) + .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) } /// Gets the output path where a compilation artifact of the given type @@ -1821,7 +1898,10 @@ fn parse_output_types( for output_type in list.split(',') { let (shorthand, path) = match output_type.split_once('=') { None => (output_type, None), - Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)), + Some((shorthand, path)) => { + (shorthand, Some(OutFileName::Real(PathBuf::from(path)))) + } }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { early_error( @@ -2892,7 +2972,7 @@ pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, - OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks, + OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; @@ -2990,6 +3070,7 @@ pub(crate) mod dep_tracking { SourceFileHashAlgorithm, TrimmedDefPaths, Option, + OutFileName, OutputType, RealFileName, LocationDetail, diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index fdb9fae44e153..2088744bc5bff 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,5 +1,5 @@ //! Related to out filenames of compilation (e.g. save analysis, binaries). -use crate::config::{CrateType, Input, OutputFilenames, OutputType}; +use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, InvalidCharacterInCrateName, @@ -8,14 +8,14 @@ use crate::Session; use rustc_ast::{self as ast, attr}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use std::path::{Path, PathBuf}; +use std::path::Path; pub fn out_filename( sess: &Session, crate_type: CrateType, outputs: &OutputFilenames, crate_name: Symbol, -) -> PathBuf { +) -> OutFileName { let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); let out_filename = outputs .outputs @@ -24,7 +24,9 @@ pub fn out_filename( .or_else(|| outputs.single_output_file.clone()) .unwrap_or(default_filename); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -112,7 +114,7 @@ pub fn filename_for_metadata( sess: &Session, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { // If the command-line specified the path, use that directly. if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) { return out_filename.clone(); @@ -120,12 +122,13 @@ pub fn filename_for_metadata( let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - let out_filename = outputs - .single_output_file - .clone() - .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{libname}.rmeta"))); + let out_filename = outputs.single_output_file.clone().unwrap_or_else(|| { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rmeta"))) + }); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -135,23 +138,33 @@ pub fn filename_for_input( crate_type: CrateType, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); match crate_type { - CrateType::Rlib => outputs.out_directory.join(&format!("lib{libname}.rlib")), + CrateType::Rlib => { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib"))) + } CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Staticlib => { let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Executable => { let suffix = &sess.target.exe_suffix; let out_filename = outputs.path(OutputType::Exe); - if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } + if let OutFileName::Real(ref path) = out_filename { + if suffix.is_empty() { + out_filename + } else { + OutFileName::Real(path.with_extension(&suffix[1..])) + } + } else { + out_filename + } } } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bbe52dbced071..08e2f19e11b33 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,9 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::Input; -use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; +use crate::config::{ + self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, +}; use crate::errors; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; @@ -135,7 +137,7 @@ pub struct Limits { pub struct CompilerIO { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub temps_dir: Option, } diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3be4382b0a3aa..2c15d1b11100c 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -202,6 +202,12 @@ flag](codegen-options/index.md#extra-filename). The files are written to the current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each emission type may also specify the output filename with the form `KIND=PATH`, which takes precedence over the `-o` flag. +Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout. +Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to +stdout despite it being a tty or not. This will result in an error if any +binary output type is written to stdout that is a tty. +This will also result in an error if multiple output types +would be written to stdout, because they would be all mixed together. [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html [LLVM IR]: https://llvm.org/docs/LangRef.html diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 7db100a08a106..b971037ea6778 100644 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -62,7 +62,7 @@ impl CodegenBackend for TheBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { - use rustc_session::{config::CrateType, output::out_filename}; + use rustc_session::{config::{CrateType, OutFileName}, output::out_filename}; use std::io::Write; let crate_name = codegen_results.crate_info.local_crate_name; for &crate_type in sess.opts.crate_types.iter() { @@ -70,8 +70,16 @@ impl CodegenBackend for TheBackend { sess.fatal(format!("Crate type is {:?}", crate_type)); } let output_name = out_filename(sess, crate_type, &outputs, crate_name); - let mut out_file = ::std::fs::File::create(output_name).unwrap(); - write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + match output_name { + OutFileName::Real(ref path) => { + let mut out_file = ::std::fs::File::create(path).unwrap(); + write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + } + OutFileName::Stdout => { + let mut stdout = std::io::stdout(); + write!(stdout, "This has been \"compiled\" successfully.").unwrap(); + } + } } Ok(()) } diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 53ec79e477bd4..6d08cfd07f89e 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/run-make-fulldeps/issue-19371/foo.rs @@ -6,7 +6,7 @@ extern crate rustc_session; extern crate rustc_span; use rustc_interface::interface; -use rustc_session::config::{Input, Options, OutputType, OutputTypes}; +use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes}; use rustc_span::source_map::FileName; use std::path::PathBuf; @@ -50,7 +50,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { crate_cfg: Default::default(), crate_check_cfg: Default::default(), input, - output_file: Some(output), + output_file: Some(OutFileName::Real(output)), output_dir: None, file_loader: None, locale_resources: &[], diff --git a/tests/run-make/emit-to-stdout/Makefile b/tests/run-make/emit-to-stdout/Makefile new file mode 100644 index 0000000000000..b7455965cffa2 --- /dev/null +++ b/tests/run-make/emit-to-stdout/Makefile @@ -0,0 +1,51 @@ +include ../tools.mk + +SRC=test.rs +OUT=$(TMPDIR)/out + +all: asm llvm-ir dep-info mir llvm-bc obj metadata link multiple-types multiple-types-option-o + +asm: $(OUT) + $(RUSTC) --emit asm=$(OUT)/$@ $(SRC) + $(RUSTC) --emit asm=- $(SRC) | diff - $(OUT)/$@ +llvm-ir: $(OUT) + $(RUSTC) --emit llvm-ir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit llvm-ir=- $(SRC) | diff - $(OUT)/$@ +dep-info: $(OUT) + $(RUSTC) -Z dep-info-omit-d-target=yes --emit dep-info=$(OUT)/$@ $(SRC) + $(RUSTC) --emit dep-info=- $(SRC) | diff - $(OUT)/$@ +mir: $(OUT) + $(RUSTC) --emit mir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit mir=- $(SRC) | diff - $(OUT)/$@ + +llvm-bc: $(OUT) + $(RUSTC) --emit llvm-bc=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-llvm-bc.stderr +obj: $(OUT) + $(RUSTC) --emit obj=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-obj.stderr + +# For metadata output, a temporary directory will be created to hold the temporary +# metadata file. But when output is stdout, the temporary directory will be located +# in the same place as $(SRC), which is mounted as read-only in the tests. Thus as +# a workaround, $(SRC) is copied to the test output directory $(OUT) and we compile +# it there. +metadata: $(OUT) + cp $(SRC) $(OUT) + (cd $(OUT); pwd; ls -d; $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) + diff $(OUT)/$@ emit-metadata.stderr + +link: $(OUT) + $(RUSTC) --emit link=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-link.stderr + +multiple-types: $(OUT) + $(RUSTC) --emit asm=- --emit llvm-ir=- --emit dep-info=- --emit mir=- $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +multiple-types-option-o: $(OUT) + $(RUSTC) -o - --emit asm,llvm-ir,dep-info,mir $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +$(OUT): + mkdir -p $(OUT) diff --git a/tests/run-make/emit-to-stdout/emit-link.stderr b/tests/run-make/emit-to-stdout/emit-link.stderr new file mode 100644 index 0000000000000..a9d856503e629 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-link.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `link` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr new file mode 100644 index 0000000000000..7b53bd16e7a54 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `llvm-bc` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-metadata.stderr b/tests/run-make/emit-to-stdout/emit-metadata.stderr new file mode 100644 index 0000000000000..ee1e52937b987 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-metadata.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-multiple-types.stderr b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr new file mode 100644 index 0000000000000..b8a683cd9ebe6 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr @@ -0,0 +1,4 @@ +error: can't use option `-o` or `--emit` to write multiple output types to stdout + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-obj.stderr b/tests/run-make/emit-to-stdout/emit-obj.stderr new file mode 100644 index 0000000000000..b130353084415 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-obj.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `obj` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/test.rs b/tests/run-make/emit-to-stdout/test.rs new file mode 100644 index 0000000000000..c1bfaa6cab5d9 --- /dev/null +++ b/tests/run-make/emit-to-stdout/test.rs @@ -0,0 +1 @@ +#![crate_type = "rlib"] From ade6c36e5319963e34c5c35a4c0c948951c48f19 Mon Sep 17 00:00:00 2001 From: Jing Peng Date: Tue, 6 Jun 2023 17:54:34 -0400 Subject: [PATCH 744/806] fix - remove useless commands from test Makefile - do not unnecessarily remove metadata temporary files because they'll be managed by MaybeTempDir - remove unused FailedRemove error introduced by this PR --- compiler/rustc_metadata/messages.ftl | 3 --- compiler/rustc_metadata/src/errors.rs | 7 ------- compiler/rustc_metadata/src/fs.rs | 9 +-------- tests/run-make/emit-to-stdout/Makefile | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d6b08d840e38b..13b3dac85d109 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -78,9 +78,6 @@ metadata_failed_create_file = metadata_failed_create_tempdir = couldn't create a temp dir: {$err} -metadata_failed_remove = - failed to remove {$filename}: {$err} - metadata_failed_write_error = failed to write {$filename}: {$err} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index e110c68321d46..fca06c0f47c22 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -402,13 +402,6 @@ pub struct FailedCopyToStdout { pub err: Error, } -#[derive(Diagnostic)] -#[diag(metadata_failed_remove)] -pub struct FailedRemove { - pub filename: PathBuf, - pub err: Error, -} - #[derive(Diagnostic)] #[diag(metadata_binary_output_to_tty)] pub struct BinaryOutputToTty; diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 5be99c8e4c0d8..238f963ed4630 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,6 +1,6 @@ use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, - FailedCreateTempdir, FailedRemove, FailedWriteError, + FailedCreateTempdir, FailedWriteError, }; use crate::{encode_metadata, EncodedMetadata}; @@ -109,13 +109,6 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); - // Delete the temporary metadata file if output is stdout - if need_metadata_file && out_filename.is_stdout() { - if let Err(err) = fs::remove_file(&metadata_filename) { - tcx.sess.emit_err(FailedRemove { filename: metadata_filename, err }); - } - } - let need_metadata_module = metadata_kind == MetadataKind::Compressed; (metadata, need_metadata_module) diff --git a/tests/run-make/emit-to-stdout/Makefile b/tests/run-make/emit-to-stdout/Makefile index b7455965cffa2..80e9b6a4ded67 100644 --- a/tests/run-make/emit-to-stdout/Makefile +++ b/tests/run-make/emit-to-stdout/Makefile @@ -32,7 +32,7 @@ obj: $(OUT) # it there. metadata: $(OUT) cp $(SRC) $(OUT) - (cd $(OUT); pwd; ls -d; $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) + (cd $(OUT); $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) diff $(OUT)/$@ emit-metadata.stderr link: $(OUT) From 9fd6d979152db51271b12ad66ab7e8352e2bbd4f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 2 Jun 2023 14:12:05 +1000 Subject: [PATCH 745/806] Improve sorting in `debug_dump`. Currently it sorts by symbol name, which is a mangled name like `_ZN1a4main17hb29587cdb6db5f42E`, which leads to non-obvious orderings. This commit changes it to use the existing `items_in_deterministic_order`, which iterates in source code order. --- compiler/rustc_monomorphize/src/partitioning.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index e99551410e022..2909042a931d1 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -864,15 +864,10 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< cgu.size_estimate() ); - // The order of `cgu.items()` is non-deterministic; sort it by name - // to give deterministic output. - let mut items: Vec<_> = cgu.items().iter().collect(); - items.sort_by_key(|(item, _)| item.symbol_name(tcx).name); - for (item, linkage) in items { + for (item, linkage) in cgu.items_in_deterministic_order(tcx) { let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); - let size = item.size_estimate(tcx); let _ = with_no_trimmed_paths!(writeln!( s, From 392045b7e70cc68290851451994d4ea135ec3f0a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Jun 2023 12:01:53 +1000 Subject: [PATCH 746/806] Make the two loops in `internalize_symbols` have the same form. Because the next commit will merge them. --- compiler/rustc_monomorphize/src/partitioning.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 2909042a931d1..fb8c21c59f22b 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -491,11 +491,14 @@ fn internalize_symbols<'tcx>( // can internalize all candidates, since there is nowhere else they // could be used from. for cgu in codegen_units { - for candidate in &internalization_candidates { - cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default)); + for (item, linkage_and_visibility) in cgu.items_mut() { + if !internalization_candidates.contains(item) { + // This item is no candidate for internalizing, so skip it. + continue; + } + *linkage_and_visibility = (Linkage::Internal, Visibility::Default); } } - return; } From fe3b6465654d8d73f9ddd45faf02971614f5780b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Jun 2023 16:05:48 +1000 Subject: [PATCH 747/806] Merge the two loops in `internalize_symbols`. Because they have a lot of overlap. --- .../rustc_monomorphize/src/partitioning.rs | 49 +++++++------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index fb8c21c59f22b..c88df1b0b60f3 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -486,21 +486,7 @@ fn internalize_symbols<'tcx>( mono_item_placements: FxHashMap, MonoItemPlacement>, internalization_candidates: FxHashSet>, ) { - if codegen_units.len() == 1 { - // Fast path for when there is only one codegen unit. In this case we - // can internalize all candidates, since there is nowhere else they - // could be used from. - for cgu in codegen_units { - for (item, linkage_and_visibility) in cgu.items_mut() { - if !internalization_candidates.contains(item) { - // This item is no candidate for internalizing, so skip it. - continue; - } - *linkage_and_visibility = (Linkage::Internal, Visibility::Default); - } - } - return; - } + let single_codegen_unit = codegen_units.len() == 1; // For each internalization candidates in each codegen unit, check if it is // used from outside its defining codegen unit. @@ -512,21 +498,24 @@ fn internalize_symbols<'tcx>( // This item is no candidate for internalizing, so skip it. continue; } - debug_assert_eq!(mono_item_placements[item], home_cgu); - - if let Some(user_items) = cx.usage_map.get_user_items(*item) { - if user_items - .iter() - .filter_map(|user_item| { - // Some user mono items might not have been - // instantiated. We can safely ignore those. - mono_item_placements.get(user_item) - }) - .any(|placement| *placement != home_cgu) - { - // Found a user from another CGU, so skip to the next item - // without marking this one as internal. - continue; + + if !single_codegen_unit { + debug_assert_eq!(mono_item_placements[item], home_cgu); + + if let Some(user_items) = cx.usage_map.get_user_items(*item) { + if user_items + .iter() + .filter_map(|user_item| { + // Some user mono items might not have been + // instantiated. We can safely ignore those. + mono_item_placements.get(user_item) + }) + .any(|placement| *placement != home_cgu) + { + // Found a user from another CGU, so skip to the next item + // without marking this one as internal. + continue; + } } } From 8dbb3475b93a15cbd64e9b726a775ee2b31328a6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 7 Jun 2023 09:24:37 +1000 Subject: [PATCH 748/806] Split loop in `place_inlined_mono_item`. This loop is doing two different things. For inlined items, it's adding them to the CGU. For all items, it's recording them in `mono_item_placements`. This commit splits it into two separate loops. This avoids putting root mono items into `reachable`, and removes the low-value check that `roots` doesn't contain inlined mono items. --- .../rustc_monomorphize/src/partitioning.rs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index c88df1b0b60f3..737c0b035e7ab 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -416,37 +416,35 @@ enum MonoItemPlacement { fn place_inlined_mono_items<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - roots: FxHashSet>, + _roots: FxHashSet>, ) -> FxHashMap, MonoItemPlacement> { - let mut mono_item_placements = FxHashMap::default(); - - let single_codegen_unit = codegen_units.len() == 1; - for cgu in codegen_units.iter_mut() { - // Collect all items that need to be available in this codegen unit. - let mut reachable = FxHashSet::default(); + // Collect all inlined items that need to be available in this codegen unit. + let mut reachable_inlined_items = FxHashSet::default(); for root in cgu.items().keys() { - // Insert the root item itself, plus all inlined items that are - // reachable from it without going via another root item. - reachable.insert(*root); - get_reachable_inlined_items(cx.tcx, *root, cx.usage_map, &mut reachable); + // Get all inlined items that are reachable from it without going + // via another root item. + get_reachable_inlined_items(cx.tcx, *root, cx.usage_map, &mut reachable_inlined_items); } // Add all monomorphizations that are not already there. - for mono_item in reachable { - if !cgu.items().contains_key(&mono_item) { - if roots.contains(&mono_item) { - bug!("GloballyShared mono-item inlined into other CGU: {:?}", mono_item); - } + for inlined_item in reachable_inlined_items { + assert!(!cgu.items().contains_key(&inlined_item)); - // This is a CGU-private copy. - cgu.items_mut().insert(mono_item, (Linkage::Internal, Visibility::Default)); - } + // This is a CGU-private copy. + cgu.items_mut().insert(inlined_item, (Linkage::Internal, Visibility::Default)); + } + } + + let mut mono_item_placements = FxHashMap::default(); + let single_codegen_unit = codegen_units.len() == 1; + for cgu in codegen_units.iter_mut() { + for item in cgu.items().keys() { if !single_codegen_unit { // If there is more than one codegen unit, we need to keep track // in which codegen units each monomorphization is placed. - match mono_item_placements.entry(mono_item) { + match mono_item_placements.entry(*item) { Entry::Occupied(e) => { let placement = e.into_mut(); debug_assert!(match *placement { From 1defd3076473699332e1b4c424d35f0dfb28c63b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 7 Jun 2023 09:24:51 +1000 Subject: [PATCH 749/806] Remove `PlacedRootMonoItems::roots`. It's no longer used. --- compiler/rustc_monomorphize/src/partitioning.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 737c0b035e7ab..e3a226fd427fa 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -129,7 +129,6 @@ struct PlacedRootMonoItems<'tcx> { /// The codegen units, sorted by name to make things deterministic. codegen_units: Vec>, - roots: FxHashSet>, internalization_candidates: FxHashSet>, } @@ -150,7 +149,7 @@ where // In the first step, we place all regular monomorphizations into their // respective 'home' codegen unit. Regular monomorphizations are all // functions and statics defined in the local crate. - let PlacedRootMonoItems { mut codegen_units, roots, internalization_candidates } = { + let PlacedRootMonoItems { mut codegen_units, internalization_candidates } = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots"); place_root_mono_items(cx, mono_items) }; @@ -176,7 +175,7 @@ where // local functions the definition of which is marked with `#[inline]`. let mono_item_placements = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); - place_inlined_mono_items(cx, &mut codegen_units, roots) + place_inlined_mono_items(cx, &mut codegen_units) }; for cgu in &mut codegen_units { @@ -244,7 +243,6 @@ fn place_root_mono_items<'tcx, I>( where I: Iterator>, { - let mut roots = FxHashSet::default(); let mut codegen_units = FxHashMap::default(); let is_incremental_build = cx.tcx.sess.opts.incremental.is_some(); let mut internalization_candidates = FxHashSet::default(); @@ -295,7 +293,6 @@ where } codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); - roots.insert(mono_item); } // Always ensure we have at least one CGU; otherwise, if we have a @@ -308,7 +305,7 @@ where let mut codegen_units: Vec<_> = codegen_units.into_values().collect(); codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); - PlacedRootMonoItems { codegen_units, roots, internalization_candidates } + PlacedRootMonoItems { codegen_units, internalization_candidates } } // This function requires the CGUs to be sorted by name on input, and ensures @@ -416,7 +413,6 @@ enum MonoItemPlacement { fn place_inlined_mono_items<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - _roots: FxHashSet>, ) -> FxHashMap, MonoItemPlacement> { for cgu in codegen_units.iter_mut() { // Collect all inlined items that need to be available in this codegen unit. From 853345635be1c51f180174d0ed66bb85aacc2570 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 7 Jun 2023 10:59:00 +1000 Subject: [PATCH 750/806] Move `mono_item_placement` construction. It's currently created in `place_inlined_mono_items` and then used in `internalize_symbols`. This commit moves the creation to `internalize_symbols`. --- .../rustc_monomorphize/src/partitioning.rs | 80 ++++++++----------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index e3a226fd427fa..1d9c8ded349c0 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -173,7 +173,7 @@ where // monomorphizations have to go into each codegen unit. These additional // monomorphizations can be drop-glue, functions from external crates, and // local functions the definition of which is marked with `#[inline]`. - let mono_item_placements = { + { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); place_inlined_mono_items(cx, &mut codegen_units) }; @@ -188,12 +188,7 @@ where // more freedom to optimize. if !tcx.sess.link_dead_code() { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); - internalize_symbols( - cx, - &mut codegen_units, - mono_item_placements, - internalization_candidates, - ); + internalize_symbols(cx, &mut codegen_units, internalization_candidates); } let instrument_dead_code = @@ -401,19 +396,10 @@ fn merge_codegen_units<'tcx>( codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); } -/// For symbol internalization, we need to know whether a symbol/mono-item is -/// used from outside the codegen unit it is defined in. This type is used -/// to keep track of that. -#[derive(Clone, PartialEq, Eq, Debug)] -enum MonoItemPlacement { - SingleCgu { cgu_name: Symbol }, - MultipleCgus, -} - fn place_inlined_mono_items<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], -) -> FxHashMap, MonoItemPlacement> { +) { for cgu in codegen_units.iter_mut() { // Collect all inlined items that need to be available in this codegen unit. let mut reachable_inlined_items = FxHashSet::default(); @@ -432,33 +418,6 @@ fn place_inlined_mono_items<'tcx>( } } - let mut mono_item_placements = FxHashMap::default(); - let single_codegen_unit = codegen_units.len() == 1; - - for cgu in codegen_units.iter_mut() { - for item in cgu.items().keys() { - if !single_codegen_unit { - // If there is more than one codegen unit, we need to keep track - // in which codegen units each monomorphization is placed. - match mono_item_placements.entry(*item) { - Entry::Occupied(e) => { - let placement = e.into_mut(); - debug_assert!(match *placement { - MonoItemPlacement::SingleCgu { cgu_name } => cgu_name != cgu.name(), - MonoItemPlacement::MultipleCgus => true, - }); - *placement = MonoItemPlacement::MultipleCgus; - } - Entry::Vacant(e) => { - e.insert(MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }); - } - } - } - } - } - - return mono_item_placements; - fn get_reachable_inlined_items<'tcx>( tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, @@ -477,11 +436,42 @@ fn place_inlined_mono_items<'tcx>( fn internalize_symbols<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - mono_item_placements: FxHashMap, MonoItemPlacement>, internalization_candidates: FxHashSet>, ) { + /// For symbol internalization, we need to know whether a symbol/mono-item + /// is used from outside the codegen unit it is defined in. This type is + /// used to keep track of that. + #[derive(Clone, PartialEq, Eq, Debug)] + enum MonoItemPlacement { + SingleCgu { cgu_name: Symbol }, + MultipleCgus, + } + + let mut mono_item_placements = FxHashMap::default(); let single_codegen_unit = codegen_units.len() == 1; + if !single_codegen_unit { + for cgu in codegen_units.iter_mut() { + for item in cgu.items().keys() { + // If there is more than one codegen unit, we need to keep track + // in which codegen units each monomorphization is placed. + match mono_item_placements.entry(*item) { + Entry::Occupied(e) => { + let placement = e.into_mut(); + debug_assert!(match *placement { + MonoItemPlacement::SingleCgu { cgu_name } => cgu_name != cgu.name(), + MonoItemPlacement::MultipleCgus => true, + }); + *placement = MonoItemPlacement::MultipleCgus; + } + Entry::Vacant(e) => { + e.insert(MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }); + } + } + } + } + } + // For each internalization candidates in each codegen unit, check if it is // used from outside its defining codegen unit. for cgu in codegen_units { From 5eafab30ba0721451f5114c5b27b37870bb3955a Mon Sep 17 00:00:00 2001 From: bohan Date: Wed, 7 Jun 2023 00:26:16 +0800 Subject: [PATCH 751/806] feat(expand): emit note for doc comment in macro matcher --- compiler/rustc_expand/src/mbe/macro_rules.rs | 46 +++++++++++++++----- tests/ui/macros/issue-112342-1.rs | 13 ++++++ tests/ui/macros/issue-112342-1.stderr | 37 +++++++++++++++- tests/ui/macros/issue-112342-2.rs | 11 +++++ tests/ui/macros/issue-112342-2.stderr | 24 ++++++++++ 5 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 tests/ui/macros/issue-112342-2.stderr diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 576d636d489c2..ee9616a0f0a9d 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -628,6 +628,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) // after parsing/expansion. we can report every error in every macro this way. } +fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool { + if seq.separator.is_some() { + false + } else { + let mut is_empty = true; + let mut iter = seq.tts.iter().peekable(); + while let Some(tt) = iter.next() { + match tt { + mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {} + mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => { + let mut now = t; + while let Some(&mbe::TokenTree::Token( + next @ Token { kind: DocComment(..), .. }, + )) = iter.peek() + { + now = next; + iter.next(); + } + let span = t.span.to(now.span); + sess.span_diagnostic.span_note_without_error( + span, + "doc comments are ignored in matcher position", + ); + } + mbe::TokenTree::Sequence(_, sub_seq) + if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore + || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {} + _ => is_empty = false, + } + } + is_empty + } +} + /// Checks that the lhs contains no repetition which could match an empty token /// tree, because then the matcher would hang indefinitely. fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { @@ -644,17 +678,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { } } TokenTree::Sequence(span, seq) => { - if seq.separator.is_none() - && seq.tts.iter().all(|seq_tt| match seq_tt { - TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, - TokenTree::Token(t) => matches!(t, Token { kind: DocComment(..), .. }), - TokenTree::Sequence(_, sub_seq) => { - sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore - || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne - } - _ => false, - }) - { + if is_empty_token_tree(sess, seq) { let sp = span.entire(); sess.span_diagnostic.span_err(sp, "repetition matches empty token tree"); return false; diff --git a/tests/ui/macros/issue-112342-1.rs b/tests/ui/macros/issue-112342-1.rs index 14fe9bbd97a24..bd2abe7f697f4 100644 --- a/tests/ui/macros/issue-112342-1.rs +++ b/tests/ui/macros/issue-112342-1.rs @@ -33,4 +33,17 @@ macro_rules! m3 { m3! {} + +macro_rules! m4 { + ( + $( + /// + /// + )* + //~^^^^ERROR repetition matches empty token tree + ) => {}; +} + +m4! {} + fn main() {} diff --git a/tests/ui/macros/issue-112342-1.stderr b/tests/ui/macros/issue-112342-1.stderr index 1ba7c0ffd3bd1..f2d82bf599e99 100644 --- a/tests/ui/macros/issue-112342-1.stderr +++ b/tests/ui/macros/issue-112342-1.stderr @@ -1,3 +1,9 @@ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:6:13 + | +LL | /// + | ^^^ + error: repetition matches empty token tree --> $DIR/issue-112342-1.rs:5:10 | @@ -7,6 +13,12 @@ LL | | /// LL | | )* | |_________^ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:17:13 + | +LL | /// + | ^^^ + error: repetition matches empty token tree --> $DIR/issue-112342-1.rs:16:10 | @@ -16,6 +28,12 @@ LL | | /// LL | | )+ | |_________^ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:28:13 + | +LL | /// + | ^^^ + error: repetition matches empty token tree --> $DIR/issue-112342-1.rs:27:10 | @@ -25,5 +43,22 @@ LL | | /// LL | | )? | |_________^ -error: aborting due to 3 previous errors +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:40:13 + | +LL | / /// +LL | | /// + | |_______________^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:39:10 + | +LL | $( + | __________^ +LL | | /// +LL | | /// +LL | | )* + | |_________^ + +error: aborting due to 4 previous errors diff --git a/tests/ui/macros/issue-112342-2.rs b/tests/ui/macros/issue-112342-2.rs index 1e1d953fa5269..e797aff94d2f3 100644 --- a/tests/ui/macros/issue-112342-2.rs +++ b/tests/ui/macros/issue-112342-2.rs @@ -25,4 +25,15 @@ macro_rules! m2 { m2! {} +macro_rules! m3 { + ( + $( + /// + $tt: tt, + )* + ) => {}; +} + +m3! {} + fn main() {} diff --git a/tests/ui/macros/issue-112342-2.stderr b/tests/ui/macros/issue-112342-2.stderr new file mode 100644 index 0000000000000..8c1b6f9471bce --- /dev/null +++ b/tests/ui/macros/issue-112342-2.stderr @@ -0,0 +1,24 @@ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:8:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:19:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:21:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:31:13 + | +LL | /// + | ^^^ + From 14ed9fa01c48ba6b97e919193c95d6445078be6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 3 Jun 2023 22:58:55 +0200 Subject: [PATCH 752/806] Avoid one `rustc` rebuild in the optimized build pipeline --- src/bootstrap/compile.rs | 4 ++ src/ci/stage-build.py | 144 ++++++++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 55 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 48685f7a9d5ac..11ac88cdec118 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -118,6 +118,10 @@ impl Step for Std { || builder.config.keep_stage_std.contains(&compiler.stage) { builder.info("Warning: Using a potentially old libstd. This may not behave well."); + + copy_third_party_objects(builder, &compiler, target); + copy_self_contained_objects(builder, &compiler, target); + builder.ensure(StdLink::from_std(self, compiler)); return; } diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index 91bd137085ee6..45f13880a4d40 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -620,11 +620,17 @@ def get_files(directory: Path, filter: Optional[Callable[[Path], bool]] = None) yield path -def build_rustc( +def bootstrap_build( pipeline: Pipeline, args: List[str], - env: Optional[Dict[str, str]] = None + env: Optional[Dict[str, str]] = None, + targets: Iterable[str] = ("library/std", ) ): + if env is None: + env = {} + else: + env = dict(env) + env["RUST_BACKTRACE"] = "1" arguments = [ sys.executable, pipeline.checkout_path() / "x.py", @@ -632,8 +638,7 @@ def build_rustc( "--target", PGO_HOST, "--host", PGO_HOST, "--stage", "2", - "library/std" - ] + args + ] + list(targets) + args cmd(arguments, env=env) @@ -776,18 +781,18 @@ def record_metrics(pipeline: Pipeline, timer: Timer): if metrics is None: return llvm_steps = tuple(metrics.find_all_by_type("bootstrap::llvm::Llvm")) - assert len(llvm_steps) > 0 llvm_duration = sum(step.duration for step in llvm_steps) rustc_steps = tuple(metrics.find_all_by_type("bootstrap::compile::Rustc")) - assert len(rustc_steps) > 0 rustc_duration = sum(step.duration for step in rustc_steps) # The LLVM step is part of the Rustc step - rustc_duration -= llvm_duration + rustc_duration = max(0, rustc_duration - llvm_duration) - timer.add_duration("LLVM", llvm_duration) - timer.add_duration("Rustc", rustc_duration) + if llvm_duration > 0: + timer.add_duration("LLVM", llvm_duration) + if rustc_duration > 0: + timer.add_duration("Rustc", rustc_duration) log_metrics(metrics) @@ -872,79 +877,108 @@ def extract_dist_dir(name: str) -> Path: )) -def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]): +def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, dist_build_args: List[str]): # Clear and prepare tmp directory shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True) os.makedirs(pipeline.opt_artifacts(), exist_ok=True) pipeline.build_rustc_perf() - # Stage 1: Build rustc + PGO instrumented LLVM - with timer.section("Stage 1 (LLVM PGO)") as stage1: - with stage1.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ - "--llvm-profile-generate" - ], env=dict( - LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p") - )) - record_metrics(pipeline, rustc_build) + """ + Stage 1: Build PGO instrumented rustc + + We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the same time + can cause issues. + """ + with timer.section("Stage 1 (rustc PGO)") as stage1: + with stage1.section("Build PGO instrumented rustc and LLVM") as rustc_pgo_instrument: + bootstrap_build(pipeline, args=[ + "--rust-profile-generate", + pipeline.rustc_profile_dir_root() + ]) + record_metrics(pipeline, rustc_pgo_instrument) with stage1.section("Gather profiles"): - gather_llvm_profiles(pipeline, runner) + gather_rustc_profiles(pipeline, runner) print_free_disk_space(pipeline) - clear_llvm_files(pipeline) - final_build_args += [ - "--llvm-profile-use", - pipeline.llvm_profile_merged_file() - ] - - # Stage 2: Build PGO instrumented rustc + LLVM - with timer.section("Stage 2 (rustc PGO)") as stage2: - with stage2.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ - "--rust-profile-generate", - pipeline.rustc_profile_dir_root() + with stage1.section("Build PGO optimized rustc") as rustc_pgo_use: + bootstrap_build(pipeline, args=[ + "--rust-profile-use", + pipeline.rustc_profile_merged_file() ]) - record_metrics(pipeline, rustc_build) + record_metrics(pipeline, rustc_pgo_use) + dist_build_args += [ + "--rust-profile-use", + pipeline.rustc_profile_merged_file() + ] + + """ + Stage 2: Gather LLVM PGO profiles + """ + with timer.section("Stage 2 (LLVM PGO)") as stage2: + # Clear normal LLVM artifacts + clear_llvm_files(pipeline) + + with stage2.section("Build PGO instrumented LLVM") as llvm_pgo_instrument: + bootstrap_build(pipeline, args=[ + "--llvm-profile-generate", + # We want to keep the already built PGO-optimized `rustc`. + "--keep-stage", "0", + "--keep-stage", "1" + ], env=dict( + LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p") + )) + record_metrics(pipeline, llvm_pgo_instrument) with stage2.section("Gather profiles"): - gather_rustc_profiles(pipeline, runner) + gather_llvm_profiles(pipeline, runner) + + dist_build_args += [ + "--llvm-profile-use", + pipeline.llvm_profile_merged_file(), + ] print_free_disk_space(pipeline) - clear_llvm_files(pipeline) - final_build_args += [ - "--rust-profile-use", - pipeline.rustc_profile_merged_file() - ] + # Clear PGO-instrumented LLVM artifacts + clear_llvm_files(pipeline) + + """ + Stage 3: Build BOLT instrumented LLVM - # Stage 3: Build rustc + BOLT instrumented LLVM + We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles. + Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build. + BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc, + therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused. + """ if pipeline.supports_bolt(): with timer.section("Stage 3 (LLVM BOLT)") as stage3: - with stage3.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ + with stage3.section("Build BOLT instrumented LLVM") as llvm_bolt_instrument: + bootstrap_build(pipeline, args=[ "--llvm-profile-use", pipeline.llvm_profile_merged_file(), "--llvm-bolt-profile-generate", - "--rust-profile-use", - pipeline.rustc_profile_merged_file() + # We want to keep the already built PGO-optimized `rustc`. + "--keep-stage", "0", + "--keep-stage", "1" ]) - record_metrics(pipeline, rustc_build) + record_metrics(pipeline, llvm_bolt_instrument) with stage3.section("Gather profiles"): gather_llvm_bolt_profiles(pipeline, runner) - # LLVM is not being cleared here, we want to reuse the previous build - print_free_disk_space(pipeline) - final_build_args += [ - "--llvm-bolt-profile-use", - pipeline.llvm_bolt_profile_merged_file() - ] + dist_build_args += [ + "--llvm-bolt-profile-use", + pipeline.llvm_bolt_profile_merged_file() + ] + print_free_disk_space(pipeline) - # Stage 4: Build PGO optimized rustc + PGO/BOLT optimized LLVM - with timer.section("Stage 4 (final build)") as stage4: - cmd(final_build_args) - record_metrics(pipeline, stage4) + """ + Final stage: Build PGO optimized rustc + PGO/BOLT optimized LLVM + """ + with timer.section("Final stage (dist build)") as final_stage: + cmd(dist_build_args) + record_metrics(pipeline, final_stage) # Try builds can be in various broken states, so we don't want to gatekeep them with tests if not is_try_build(): From a2ab47f1e57a7192bbdb7f6e90e7776a458b3315 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 7 Jun 2023 05:53:26 -0500 Subject: [PATCH 753/806] download-rustc: Fix `x test core` on MacOS before, this hardcoded `.so` as the extension for dynamically linked objects, which is incorrect everywhere except linux --- src/bootstrap/compile.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 48685f7a9d5ac..a9e15d8924605 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -1358,7 +1358,12 @@ impl Step for Sysroot { // newly compiled std, not the downloaded std. add_filtered_files("lib", builder.config.ci_rust_std_contents()); - let filtered_extensions = [OsStr::new("rmeta"), OsStr::new("rlib"), OsStr::new("so")]; + let filtered_extensions = [ + OsStr::new("rmeta"), + OsStr::new("rlib"), + // FIXME: this is wrong when compiler.host != build, but we don't support that today + OsStr::new(std::env::consts::DLL_EXTENSION), + ]; let ci_rustc_dir = builder.ci_rustc_dir(builder.config.build); builder.cp_filtered(&ci_rustc_dir, &sysroot, &|path| { if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { From 3490a510d552390f1970cb36d5ca76569f571a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 4 Nov 2022 17:05:56 +0100 Subject: [PATCH 754/806] rustdoc: re-elide cross-crate default trait object lifetime bounds --- src/librustdoc/clean/auto_trait.rs | 2 +- src/librustdoc/clean/blanket_impl.rs | 8 +- src/librustdoc/clean/inline.rs | 19 +- src/librustdoc/clean/mod.rs | 245 ++++++++++++++++-- src/librustdoc/clean/utils.rs | 43 +-- tests/rustdoc/assoc-consts.rs | 1 - .../inline_cross/auxiliary/dyn_trait.rs | 60 ++++- tests/rustdoc/inline_cross/dyn_trait.rs | 125 ++++++++- 8 files changed, 435 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index baf2b0a858529..c8a40e0150112 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -124,7 +124,7 @@ where unsafety: hir::Unsafety::Normal, generics: new_generics, trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), items: Vec::new(), polarity, kind: ImplKind::Auto, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 9183fdaa08704..a36041588510f 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -107,7 +107,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ty::Binder::dummy(trait_ref.subst_identity()), ThinVec::new(), )), - for_: clean_middle_ty(ty::Binder::dummy(ty.subst_identity()), cx, None), + for_: clean_middle_ty( + ty::Binder::dummy(ty.subst_identity()), + cx, + None, + None, + ), items: cx .tcx .associated_items(impl_def_id) @@ -119,6 +124,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ty::Binder::dummy(trait_ref.subst_identity().self_ty()), cx, None, + None, ))), }))), cfg: None, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 7dc08b3b1ffa6..3aa98da1c803a 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -278,8 +278,12 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = - clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did)); + let type_ = clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ); Box::new(clean::Typedef { type_, @@ -386,9 +390,12 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), - None => { - clean_middle_ty(ty::Binder::dummy(tcx.type_of(did).subst_identity()), cx, Some(did)) - } + None => clean_middle_ty( + ty::Binder::dummy(tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ), }; // Only inline impl if the implementing type is @@ -630,6 +637,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: clean::ConstantKind::Extern { def_id }, } @@ -641,6 +649,7 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did), + None, ), mutability: if mutable { Mutability::Mut } else { Mutability::Not }, expr: None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b56b81279962b..526c9a4a35fae 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -253,6 +253,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: ConstantKind::Anonymous { body: constant.value.body }, } @@ -264,7 +265,7 @@ pub(crate) fn clean_middle_const<'tcx>( ) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { - type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None), + type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -370,7 +371,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None), + ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None, None), bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) @@ -396,7 +397,7 @@ fn clean_type_outlives_predicate<'tcx>( let ty::OutlivesPredicate(ty, lt) = pred.skip_binder(); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(pred.rebind(ty), cx, None), + ty: clean_middle_ty(pred.rebind(ty), cx, None, None), bounds: vec![GenericBound::Outlives( clean_middle_region(lt).expect("failed to clean lifetimes"), )], @@ -409,7 +410,7 @@ fn clean_middle_term<'tcx>( cx: &mut DocContext<'tcx>, ) -> Term { match term.skip_binder().unpack() { - ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None)), + ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), } } @@ -462,7 +463,7 @@ fn clean_projection<'tcx>( let trait_ = clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new()); - let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) } else { @@ -493,8 +494,13 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: substs_to_args(cx, ty.map_bound(|ty| &ty.substs[generics.parent_count..]), false) - .into(), + args: substs_to_args( + cx, + ty.map_bound(|ty| &ty.substs[generics.parent_count..]), + false, + None, + ) + .into(), bindings: Default::default(), }, } @@ -514,6 +520,7 @@ fn clean_generic_param_def<'tcx>( ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()), cx, Some(def.def_id), + None, )) } else { None @@ -540,6 +547,7 @@ fn clean_generic_param_def<'tcx>( ), cx, Some(def.def_id), + None, )), default: match has_default { true => Some(Box::new( @@ -871,7 +879,7 @@ fn clean_ty_generics<'tcx>( let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs, bound_params) in proj { - let rhs = clean_middle_ty(rhs, cx, None); + let rhs = clean_middle_ty(rhs, cx, None, None); simplify::merge_bounds( cx, &mut bounds, @@ -1126,7 +1134,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let output = clean_middle_ty(sig.output(), cx, None); + let output = clean_middle_ty(sig.output(), cx, None, None); FnDecl { output, @@ -1136,7 +1144,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( .inputs() .iter() .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None), + type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), name: names .next() .map(|i| i.name) @@ -1190,8 +1198,12 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_: clean_ty(default, cx), @@ -1230,8 +1242,12 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_, generics, item_type: Some(item_type) }), Vec::new(), @@ -1254,6 +1270,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ); let provided = match assoc_item.container { @@ -1447,6 +1464,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics, // FIXME: should we obtain the Type from HIR and pass it on here? @@ -1465,6 +1483,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics: Generics { params: ThinVec::new(), @@ -1510,7 +1529,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if !ty.has_escaping_bound_vars() && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) { - return clean_middle_ty(normalized_value, cx, None); + return clean_middle_ty(normalized_value, cx, None, None); } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -1738,11 +1757,174 @@ fn normalize<'tcx>( } } +fn clean_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option>, + trait_: DefId, + substs: ty::Binder<'tcx, &ty::List>>, + tcx: TyCtxt<'tcx>, +) -> Option { + if can_elide_trait_object_lifetime_bound(region, container, trait_, substs, tcx) { + return None; + } + + // Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object + // lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the + // latter contrary to `clean_middle_region`. + match *region { + ty::ReStatic => Some(Lifetime::statik()), + ty::ReEarlyBound(region) if region.name != kw::Empty => Some(Lifetime(region.name)), + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) + if name != kw::Empty => + { + Some(Lifetime(name)) + } + ty::ReEarlyBound(_) + | ty::ReLateBound(..) + | ty::ReFree(_) + | ty::ReVar(_) + | ty::RePlaceholder(_) + | ty::ReErased + | ty::ReError(_) => None, + } +} + +fn can_elide_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option>, + trait_: DefId, + substs: ty::Binder<'tcx, &ty::List>>, + tcx: TyCtxt<'tcx>, +) -> bool { + // Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes + + // > If the trait object is used as a type argument of a generic type then the containing type is + // > first used to try to infer a bound. + let default = container + .map_or(ObjectLifetimeDefault::Empty, |container| container.object_lifetime_default(tcx)); + + // > If there is a unique bound from the containing type then that is the default + // If there is a default object lifetime and the given region is lexically equal to it, elide it. + match default { + ObjectLifetimeDefault::Static => return *region == ty::ReStatic, + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(), + // > If there is more than one bound from the containing type then an explicit bound must be specified + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + ObjectLifetimeDefault::Ambiguous => return false, + // There is no meaningful bound. Further processing is needed... + ObjectLifetimeDefault::Empty => {} + } + + // We filter out any escaping regions below, thus it's fine to skip the binder. + let substs = substs.skip_binder(); + + // > If neither of those rules apply, then the bounds on the trait are used: + let mut trait_regions: Vec<_> = tcx + .predicates_of(trait_) + .predicates + .iter() + .filter_map(|(pred, _)| { + // Look for bounds of the form `Self: 'a` for any region `'a`. + if let ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, region))) = pred.kind().skip_binder() + && let ty::Param(param) = ty.kind() + && param.name == kw::SelfUpper + { + Some(ty::EarlyBinder::bind(region).subst(tcx, tcx.mk_substs_trait(ty, substs))) + .filter(|region| !region.has_escaping_bound_vars()) + } else { + None + } + }) + .collect(); + + // As a result of the substitutions above, we might be left with duplicate regions. + // Consider `<'a, 'b> Self: 'a + 'b` with substitution `<'r, 'r>`. Deduplicate. + trait_regions.dedup(); + + // > If 'static is used for any lifetime bound then 'static is used. + // If the list contains `'static`, throw out everyhing else as it outlives any of them. + if let Some(index) = trait_regions.iter().position(|region| region.is_static()) { + let static_ = trait_regions.swap_remove(index); + trait_regions.clear(); + trait_regions.push(static_); + } + + match *trait_regions { + // > If the trait has no lifetime bounds, then the lifetime is inferred in expressions + // > and is 'static outside of expressions. + // FIXME: If we are in an expression context (i.e. fn bodies and const exprs) then the default is + // `'_` and not `'static`. Only if we are in a non-expression one, the default is `'static`. + // Note however that at the time of this writing it should be fine to disregard this subtlety + // as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead) + // nor show the contents of fn bodies. + [] => *region == ty::ReStatic, + // > If the trait is defined with a single lifetime bound then that bound is used. + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + [trait_region] => trait_region.get_name() == region.get_name(), + // There are several distinct trait regions and none are `'static` (thanks to the preprocessing above). + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + _ => false, + } +} + +#[derive(Debug)] +pub(crate) enum ContainerTy<'tcx> { + Ref(ty::Region<'tcx>), + Regular { ty: DefId, substs: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, arg: usize }, +} + +impl<'tcx> ContainerTy<'tcx> { + fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { + match self { + Self::Ref(region) => ObjectLifetimeDefault::Arg(region), + Self::Regular { ty: container, substs, arg: index } => { + let (DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait + | DefKind::AssocTy + | DefKind::Variant) = tcx.def_kind(container) + else { + return ObjectLifetimeDefault::Empty; + }; + + let generics = tcx.generics_of(container); + let param = generics.params[index].def_id; + let default = tcx.object_lifetime_default(param); + + match default { + rbv::ObjectLifetimeDefault::Param(lifetime) => { + let index = generics.param_def_id_to_index[&lifetime]; + let arg = substs.skip_binder()[index as usize].expect_region(); + ObjectLifetimeDefault::Arg(arg) + } + rbv::ObjectLifetimeDefault::Empty => ObjectLifetimeDefault::Empty, + rbv::ObjectLifetimeDefault::Static => ObjectLifetimeDefault::Static, + rbv::ObjectLifetimeDefault::Ambiguous => ObjectLifetimeDefault::Ambiguous, + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ObjectLifetimeDefault<'tcx> { + Empty, + Static, + Ambiguous, + Arg(ty::Region<'tcx>), +} + #[instrument(level = "trace", skip(cx), ret)] pub(crate) fn clean_middle_ty<'tcx>( bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, parent_def_id: Option, + container: Option>, ) -> Type { let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty); match *bound_ty.skip_binder().kind() { @@ -1753,19 +1935,24 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Uint(uint_ty) => Primitive(uint_ty.into()), ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), - ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None))), + ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); - Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), n.into()) + Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } ty::RawPtr(mt) => { - RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None))) + RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), mutability: mutbl, - type_: Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), + type_: Box::new(clean_middle_ty( + bound_ty.rebind(ty), + cx, + None, + Some(ContainerTy::Ref(r)), + )), }, ty::FnDef(..) | ty::FnPtr(_) => { // FIXME: should we merge the outer and inner binders somehow? @@ -1817,10 +2004,8 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - // FIXME(fmease): Hide the trait-object lifetime bound if it coincides with its default - // to partially address #44306. Follow the rules outlined at - // https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes - let lifetime = clean_middle_region(*reg); + let lifetime = clean_trait_object_lifetime_bound(*reg, container, did, substs, cx.tcx); + let mut bounds = dids .map(|did| { let empty = ty::Binder::dummy(InternalSubsts::empty()); @@ -1869,7 +2054,7 @@ pub(crate) fn clean_middle_ty<'tcx>( DynTrait(bounds, lifetime) } ty::Tuple(t) => { - Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None)).collect()) + Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } ty::Alias(ty::Projection, ref data) => { @@ -1878,7 +2063,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Inherent, alias_ty) => { let alias_ty = bound_ty.rebind(alias_ty); - let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); Type::QPath(Box::new(QPathData { assoc: PathSegment { @@ -1888,6 +2073,7 @@ pub(crate) fn clean_middle_ty<'tcx>( cx, alias_ty.map_bound(|ty| ty.substs.as_slice()), true, + None, ) .into(), bindings: Default::default(), @@ -2023,6 +2209,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()), cx, Some(field.did), + None, ), cx, ) @@ -2314,7 +2501,12 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let ty = clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let ty = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -2418,6 +2610,7 @@ fn clean_impl<'tcx>( ty::Binder::dummy(tcx.type_of(def_id).subst_identity()), cx, Some(def_id.to_def_id()), + None, )), _ => None, }); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 366f93952963f..294de12cea8e2 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -73,8 +73,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn substs_to_args<'tcx>( cx: &mut DocContext<'tcx>, - substs: ty::Binder<'tcx, &[ty::subst::GenericArg<'tcx>]>, + substs: ty::Binder<'tcx, &'tcx [ty::subst::GenericArg<'tcx>]>, mut skip_first: bool, + container: Option, ) -> Vec { let mut ret_val = Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first { @@ -82,19 +83,29 @@ pub(crate) fn substs_to_args<'tcx>( } else { 0 })); - ret_val.extend(substs.iter().filter_map(|kind| match kind.skip_binder().unpack() { - GenericArgKind::Lifetime(lt) => { - Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) - } - GenericArgKind::Type(_) if skip_first => { - skip_first = false; - None - } - GenericArgKind::Type(ty) => { - Some(GenericArg::Type(clean_middle_ty(kind.rebind(ty), cx, None))) - } - GenericArgKind::Const(ct) => { - Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + + ret_val.extend(substs.iter().enumerate().filter_map(|(index, kind)| { + match kind.skip_binder().unpack() { + GenericArgKind::Lifetime(lt) => { + Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) + } + GenericArgKind::Type(_) if skip_first => { + skip_first = false; + None + } + GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( + kind.rebind(ty), + cx, + None, + container.map(|container| crate::clean::ContainerTy::Regular { + ty: container, + substs, + arg: index, + }), + ))), + GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + } } })); ret_val @@ -107,7 +118,7 @@ fn external_generic_args<'tcx>( bindings: ThinVec, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> GenericArgs { - let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self); + let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self, Some(did)); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { let ty = substs @@ -118,7 +129,7 @@ fn external_generic_args<'tcx>( let inputs = // The trait's first substitution is the one after self, if there is one. match ty.skip_binder().kind() { - ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None)).collect::>().into(), + ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::>().into(), _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = bindings.into_iter().next().and_then(|binding| match binding.kind { diff --git a/tests/rustdoc/assoc-consts.rs b/tests/rustdoc/assoc-consts.rs index 68a11c57b5292..08dfa879d4363 100644 --- a/tests/rustdoc/assoc-consts.rs +++ b/tests/rustdoc/assoc-consts.rs @@ -46,7 +46,6 @@ pub fn f(_: &(ToString + 'static)) {} impl Bar { // @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.F"]' \ // "const F: fn(_: &(dyn ToString + 'static))" - // FIXME(fmease): Hide default lifetime, render "const F: fn(_: &dyn ToString)" pub const F: fn(_: &(ToString + 'static)) = f; } diff --git a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs index 9ac2e3d96debd..644d0699e9d07 100644 --- a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + pub type Ty0 = dyn for<'any> FnOnce(&'any str) -> bool; pub type Ty1<'obj> = dyn std::fmt::Display + 'obj; @@ -6,12 +8,60 @@ pub type Ty2 = dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>; pub type Ty3<'s> = &'s dyn ToString; -pub fn func0(_: &(dyn Fn() + '_)) {} - -pub fn func1<'func>(_: &(dyn Fn() + 'func)) {} - pub trait Container<'r> { type Item<'a, 'ctx>; } -pub trait Shape<'a> {} +// Trait-object types inside of a container type that has lifetime bounds ("wrapped"). + +pub fn late_bound_wrapped_elided(_: &(dyn Fn() + '_)) {} +pub fn late_bound_wrapped_late0<'f>(_: &mut (dyn Fn() + 'f)) {} +pub fn late_bound_wrapped_defaulted0<'f>(_: &'f mut dyn Fn()) {} +pub type EarlyBoundWrappedDefaulted0<'x> = std::cell::Ref<'x, dyn Trait>; +pub type EarlyBoundWrappedDefaulted1<'x> = &'x dyn Trait; +pub type EarlyBoundWrappedEarly<'x, 'y> = std::cell::Ref<'x, dyn Trait + 'y>; +pub type EarlyBoundWrappedStatic<'x> = std::cell::Ref<'x, dyn Trait + 'static>; +pub fn late_bound_wrapped_defaulted1<'l>(_: std::cell::Ref<'l, dyn Trait>) {} +pub fn late_bound_wrapped_late1<'l, 'm>(_: std::cell::Ref<'l, dyn Trait + 'm>) {} +pub fn late_bound_wrapped_early<'e, 'l>(_: std::cell::Ref<'l, dyn Trait + 'e>) where 'e: {} // `'e` is early-bound +pub fn elided_bound_wrapped_defaulted(_: std::cell::Ref<'_, dyn Trait>) {} +pub type StaticBoundWrappedDefaulted0 = std::cell::Ref<'static, dyn Trait>; +pub type StaticBoundWrappedDefaulted1 = &'static dyn Trait; +pub type AmbiguousBoundWrappedEarly0<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>; +pub type AmbiguousBoundWrappedEarly1<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>; +pub type AmbiguousBoundWrappedStatic<'q> = AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>; + +// Trait-object types inside of a container type that doesn't have lifetime bounds ("wrapped"). + +pub type NoBoundsWrappedDefaulted = Box; +pub type NoBoundsWrappedEarly<'e> = Box; +pub fn no_bounds_wrapped_late<'l>(_: Box) {} +pub fn no_bounds_wrapped_elided(_: Box) {} + +// Trait-object types outside of a container (“bare”). + +pub type BareNoBoundsDefaulted = dyn Trait; +pub type BareNoBoundsEarly<'p> = dyn Trait + 'p; +pub type BareEarlyBoundDefaulted0<'u> = dyn EarlyBoundTrait0<'u>; +pub type BareEarlyBoundDefaulted1 = dyn for<'any> EarlyBoundTrait0<'any>; +pub type BareEarlyBoundDefaulted2<'w> = dyn EarlyBoundTrait1<'static, 'w>; +pub type BareEarlyBoundEarly<'i, 'j> = dyn EarlyBoundTrait0<'i> + 'j; +pub type BareEarlyBoundStatic<'i> = dyn EarlyBoundTrait0<'i> + 'static; +pub type BareStaticBoundDefaulted = dyn StaticBoundTrait; +pub type BareHigherRankedBoundDefaulted0 = dyn HigherRankedBoundTrait0; +pub type BareHigherRankedBoundDefaulted1<'r> = dyn HigherRankedBoundTrait1<'r>; +pub type BareAmbiguousBoundEarly0<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'm; +pub type BareAmbiguousBoundEarly1<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'n; +pub type BareAmbiguousBoundStatic<'o> = dyn AmbiguousBoundTrait<'o, 'o> + 'static; + +// Trait and container definitions. + +pub trait Trait {} // no bounds +pub trait EarlyBoundTrait0<'b>: 'b {} +pub trait EarlyBoundTrait1<'unused, 'c>: 'c {} +pub trait StaticBoundTrait: 'static {} +pub trait HigherRankedBoundTrait0 where for<'a> Self: 'a {} +pub trait HigherRankedBoundTrait1<'e> where for<'l> Self: 'e + 'l {} +pub trait AmbiguousBoundTrait<'a, 'b>: 'a + 'b {} + +pub struct AmbiguousBoundWrapper<'a, 'b, T: ?Sized + 'a + 'b>(&'a T, &'b T); diff --git a/tests/rustdoc/inline_cross/dyn_trait.rs b/tests/rustdoc/inline_cross/dyn_trait.rs index 649d98f71396a..1de01af83d13e 100644 --- a/tests/rustdoc/inline_cross/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/dyn_trait.rs @@ -1,31 +1,130 @@ #![crate_name = "user"] +// In each test case, we include the trailing semicolon to ensure that nothing extra comes +// after the type like an unwanted outlives-bound. + // aux-crate:dyn_trait=dyn_trait.rs // edition:2021 // @has user/type.Ty0.html -// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool + 'static" -// FIXME(fmease): Hide default lifetime bound `'static` +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool;" pub use dyn_trait::Ty0; // @has user/type.Ty1.html -// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj" +// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj;" pub use dyn_trait::Ty1; // @has user/type.Ty2.html -// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>" +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>;" pub use dyn_trait::Ty2; // @has user/type.Ty3.html -// @has - '//*[@class="rust item-decl"]//code' "&'s (dyn ToString + 's)" -// FIXME(fmease): Hide default lifetime bound, render "&'s dyn ToString" +// @has - '//*[@class="rust item-decl"]//code' "&'s dyn ToString;" pub use dyn_trait::Ty3; -// @has user/fn.func0.html -// @has - '//pre[@class="rust item-decl"]' "func0(_: &dyn Fn())" -// FIXME(fmease): Show placeholder-lifetime bound, render "func0(_: &(dyn Fn() + '_))" -pub use dyn_trait::func0; +// Below we check if we correctly elide trait-object lifetime bounds if they coincide with their +// default (known as "object lifetime default" or "default trait object lifetime"). + +// @has user/fn.lbwel.html +// @has - '//pre[@class="rust item-decl"]' "lbwel(_: &dyn Fn())" +pub use dyn_trait::late_bound_wrapped_elided as lbwel; +// @has user/fn.lbwl0.html +// has - '//pre[@class="rust item-decl"]' "lbwl0<'f>(_: &mut (dyn Fn() + 'f))" +pub use dyn_trait::late_bound_wrapped_late0 as lbwl0; +// @has user/fn.lbwd0.html +// has - '//pre[@class="rust item-decl"]' "lbwd0<'f>(_: &'f mut dyn Fn())" +pub use dyn_trait::late_bound_wrapped_defaulted0 as lbwd0; +// @has user/type.EarlyBoundWrappedDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait>;" +pub use dyn_trait::EarlyBoundWrappedDefaulted0; +// @has user/type.EarlyBoundWrappedDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "&'x dyn Trait;" +pub use dyn_trait::EarlyBoundWrappedDefaulted1; +// @has user/type.EarlyBoundWrappedEarly.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'y>" +pub use dyn_trait::EarlyBoundWrappedEarly; +// @has user/type.EarlyBoundWrappedStatic.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'static>" +pub use dyn_trait::EarlyBoundWrappedStatic; +// @has user/fn.lbwd1.html +// @has - '//pre[@class="rust item-decl"]' "lbwd1<'l>(_: Ref<'l, dyn Trait>)" +pub use dyn_trait::late_bound_wrapped_defaulted1 as lbwd1; +// @has user/fn.lbwl1.html +// @has - '//pre[@class="rust item-decl"]' "lbwl1<'l, 'm>(_: Ref<'l, dyn Trait + 'm>)" +pub use dyn_trait::late_bound_wrapped_late1 as lbwl1; +// @has user/fn.lbwe.html +// @has - '//pre[@class="rust item-decl"]' "lbwe<'e, 'l>(_: Ref<'l, dyn Trait + 'e>)" +pub use dyn_trait::late_bound_wrapped_early as lbwe; +// @has user/fn.ebwd.html +// @has - '//pre[@class="rust item-decl"]' "ebwd(_: Ref<'_, dyn Trait>)" +pub use dyn_trait::elided_bound_wrapped_defaulted as ebwd; +// @has user/type.StaticBoundWrappedDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'static, dyn Trait>;" +pub use dyn_trait::StaticBoundWrappedDefaulted0; +// @has user/type.StaticBoundWrappedDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "&'static dyn Trait;" +pub use dyn_trait::StaticBoundWrappedDefaulted1; +// @has user/type.AmbiguousBoundWrappedEarly0.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>;" +pub use dyn_trait::AmbiguousBoundWrappedEarly0; +// @has user/type.AmbiguousBoundWrappedEarly1.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>;" +pub use dyn_trait::AmbiguousBoundWrappedEarly1; +// @has user/type.AmbiguousBoundWrappedStatic.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>;" +pub use dyn_trait::AmbiguousBoundWrappedStatic; + +// @has user/type.NoBoundsWrappedDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "Box;" +pub use dyn_trait::NoBoundsWrappedDefaulted; +// @has user/type.NoBoundsWrappedEarly.html +// @has - '//*[@class="rust item-decl"]//code' "Box;" +pub use dyn_trait::NoBoundsWrappedEarly; +// @has user/fn.nbwl.html +// @has - '//pre[@class="rust item-decl"]' "nbwl<'l>(_: Box)" +pub use dyn_trait::no_bounds_wrapped_late as nbwl; +// @has user/fn.nbwel.html +// @has - '//pre[@class="rust item-decl"]' "nbwel(_: Box)" +// NB: It might seem counterintuitive to display the explicitly elided lifetime `'_` here instead of +// eliding it but this behavior is correct: The default is `'static` here which != `'_`. +pub use dyn_trait::no_bounds_wrapped_elided as nbwel; -// @has user/fn.func1.html -// @has - '//pre[@class="rust item-decl"]' "func1<'func>(_: &(dyn Fn() + 'func))" -pub use dyn_trait::func1; +// @has user/type.BareNoBoundsDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait;" +pub use dyn_trait::BareNoBoundsDefaulted; +// @has user/type.BareNoBoundsEarly.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait + 'p;" +pub use dyn_trait::BareNoBoundsEarly; +// @has user/type.BareEarlyBoundDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'u>;" +pub use dyn_trait::BareEarlyBoundDefaulted0; +// @has user/type.BareEarlyBoundDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> EarlyBoundTrait0<'any>;" +pub use dyn_trait::BareEarlyBoundDefaulted1; +// @has user/type.BareEarlyBoundDefaulted2.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait1<'static, 'w>;" +pub use dyn_trait::BareEarlyBoundDefaulted2; +// @has user/type.BareEarlyBoundEarly.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'j;" +pub use dyn_trait::BareEarlyBoundEarly; +// @has user/type.BareEarlyBoundStatic.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'static;" +pub use dyn_trait::BareEarlyBoundStatic; +// @has user/type.BareStaticBoundDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "dyn StaticBoundTrait;" +pub use dyn_trait::BareStaticBoundDefaulted; +// @has user/type.BareHigherRankedBoundDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait0;" +pub use dyn_trait::BareHigherRankedBoundDefaulted0; +// @has user/type.BareHigherRankedBoundDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait1<'r>;" +pub use dyn_trait::BareHigherRankedBoundDefaulted1; +// @has user/type.BareAmbiguousBoundEarly0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'm;" +pub use dyn_trait::BareAmbiguousBoundEarly0; +// @has user/type.BareAmbiguousBoundEarly1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'n;" +pub use dyn_trait::BareAmbiguousBoundEarly1; +// @has user/type.BareAmbiguousBoundStatic.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'o, 'o> + 'static;" +pub use dyn_trait::BareAmbiguousBoundStatic; From 5b5d84fd6a36a8ea4f39e3b730262321cc8f8622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 6 Jun 2023 20:17:07 +0200 Subject: [PATCH 755/806] use wf::object_region_bounds --- src/librustdoc/clean/mod.rs | 51 +++++++------------------------------ 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 526c9a4a35fae..0c7950bfd3ddd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -31,6 +31,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; +use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -1760,11 +1761,10 @@ fn normalize<'tcx>( fn clean_trait_object_lifetime_bound<'tcx>( region: ty::Region<'tcx>, container: Option>, - trait_: DefId, - substs: ty::Binder<'tcx, &ty::List>>, + preds: &'tcx ty::List>, tcx: TyCtxt<'tcx>, ) -> Option { - if can_elide_trait_object_lifetime_bound(region, container, trait_, substs, tcx) { + if can_elide_trait_object_lifetime_bound(region, container, preds, tcx) { return None; } @@ -1792,8 +1792,7 @@ fn clean_trait_object_lifetime_bound<'tcx>( fn can_elide_trait_object_lifetime_bound<'tcx>( region: ty::Region<'tcx>, container: Option>, - trait_: DefId, - substs: ty::Binder<'tcx, &ty::List>>, + preds: &'tcx ty::List>, tcx: TyCtxt<'tcx>, ) -> bool { // Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes @@ -1817,41 +1816,8 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( ObjectLifetimeDefault::Empty => {} } - // We filter out any escaping regions below, thus it's fine to skip the binder. - let substs = substs.skip_binder(); - // > If neither of those rules apply, then the bounds on the trait are used: - let mut trait_regions: Vec<_> = tcx - .predicates_of(trait_) - .predicates - .iter() - .filter_map(|(pred, _)| { - // Look for bounds of the form `Self: 'a` for any region `'a`. - if let ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, region))) = pred.kind().skip_binder() - && let ty::Param(param) = ty.kind() - && param.name == kw::SelfUpper - { - Some(ty::EarlyBinder::bind(region).subst(tcx, tcx.mk_substs_trait(ty, substs))) - .filter(|region| !region.has_escaping_bound_vars()) - } else { - None - } - }) - .collect(); - - // As a result of the substitutions above, we might be left with duplicate regions. - // Consider `<'a, 'b> Self: 'a + 'b` with substitution `<'r, 'r>`. Deduplicate. - trait_regions.dedup(); - - // > If 'static is used for any lifetime bound then 'static is used. - // If the list contains `'static`, throw out everyhing else as it outlives any of them. - if let Some(index) = trait_regions.iter().position(|region| region.is_static()) { - let static_ = trait_regions.swap_remove(index); - trait_regions.clear(); - trait_regions.push(static_); - } - - match *trait_regions { + match *object_region_bounds(tcx, preds) { // > If the trait has no lifetime bounds, then the lifetime is inferred in expressions // > and is 'static outside of expressions. // FIXME: If we are in an expression context (i.e. fn bodies and const exprs) then the default is @@ -1861,9 +1827,10 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( // nor show the contents of fn bodies. [] => *region == ty::ReStatic, // > If the trait is defined with a single lifetime bound then that bound is used. + // > If 'static is used for any lifetime bound then 'static is used. // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. - [trait_region] => trait_region.get_name() == region.get_name(), - // There are several distinct trait regions and none are `'static` (thanks to the preprocessing above). + [object_region] => object_region.get_name() == region.get_name(), + // There are several distinct trait regions and none are `'static`. // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. // Don't elide the lifetime. _ => false, @@ -2004,7 +1971,7 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - let lifetime = clean_trait_object_lifetime_bound(*reg, container, did, substs, cx.tcx); + let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); let mut bounds = dids .map(|did| { From 41f9f63de65a029393dfe7ec22c54a75fdf65d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 7 Jun 2023 14:16:28 +0200 Subject: [PATCH 756/806] Use ``--keep-stage` also for the final build --- src/ci/stage-build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index 45f13880a4d40..febc0492b9433 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -973,6 +973,12 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRu ] print_free_disk_space(pipeline) + # We want to keep the already built PGO-optimized `rustc`. + dist_build_args += [ + "--keep-stage", "0", + "--keep-stage", "1" + ] + """ Final stage: Build PGO optimized rustc + PGO/BOLT optimized LLVM """ From 3522baa8ba671cd2eb07f077424c0073d152aad6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 Jun 2023 17:42:04 +0200 Subject: [PATCH 757/806] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/theme-change.goml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml index ae6947213897e..e4b031b735eb0 100644 --- a/tests/rustdoc-gui/theme-change.goml +++ b/tests/rustdoc-gui/theme-change.goml @@ -3,9 +3,9 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"} reload: -store-value: (background_light, "rgb(255, 255, 255)") -store-value: (background_dark, "rgb(53, 53, 53)") -store-value: (background_ayu, "rgb(15, 20, 25)") +store-value: (background_light, "white") +store-value: (background_dark, "#353535") +store-value: (background_ayu, "#0f1419") click: "#settings-menu" wait-for: "#theme-ayu" From 053e6b80c7c5a6b6052468e7d35f6ef1d011274f Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 7 Jun 2023 17:16:34 +0000 Subject: [PATCH 758/806] Remove accidental comment --- compiler/rustc_builtin_macros/src/format.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index c59a733c05568..4c878785b7b45 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -554,9 +554,6 @@ fn report_missing_placeholders( fmt_span: Span, ) { let mut diag = if let &[(span, named)] = &unused[..] { - //let mut diag = ecx.struct_span_err(span, msg); - //diag.span_label(span, msg); - //diag ecx.create_err(errors::FormatUnusedArg { span, named }) } else { let unused_labels = From 0304e0a5b0fafa1b3be46207c4651709eec478c5 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 17 May 2023 23:53:04 +0200 Subject: [PATCH 759/806] Force all native libraries to be statically linked when linking a static binary --- compiler/rustc_codegen_ssa/src/back/link.rs | 38 ++++++++++++++++++--- compiler/rustc_target/src/spec/mod.rs | 11 ++++++ src/doc/rustc/src/command-line-arguments.md | 4 +-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ea06cb02d8baf..91598e8d879ee 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2129,7 +2129,14 @@ fn linker_with_args<'a>( cmd.add_as_needed(); // Local native libraries of all kinds. - add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_local_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Upstream rust crates and their non-dynamic native libraries. add_upstream_rust_crates( @@ -2139,10 +2146,18 @@ fn linker_with_args<'a>( codegen_results, crate_type, tmpdir, + link_output_kind, ); // Dynamic native libraries from upstream crates. - add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_upstream_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Link with the import library generated for any raw-dylib functions. for (raw_dylib_name, raw_dylib_imports) in @@ -2397,6 +2412,7 @@ fn add_native_libs_from_crate( cnum: CrateNum, link_static: bool, link_dynamic: bool, + link_output_kind: LinkOutputKind, ) { if !sess.opts.unstable_opts.link_native_libraries { // If `-Zlink-native-libraries=false` is set, then the assumption is that an @@ -2476,8 +2492,16 @@ fn add_native_libs_from_crate( } } NativeLibKind::Unspecified => { - if link_dynamic { - cmd.link_dylib(name, verbatim, true); + // If we are generating a static binary, prefer static library when the + // link kind is unspecified. + if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs { + if link_static { + cmd.link_staticlib(name, verbatim) + } + } else { + if link_dynamic { + cmd.link_dylib(name, verbatim, true); + } } } NativeLibKind::Framework { as_needed } => { @@ -2504,6 +2528,7 @@ fn add_local_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { if sess.opts.unstable_opts.link_native_libraries { // User-supplied library search paths (-L on the command line). These are the same paths @@ -2533,6 +2558,7 @@ fn add_local_native_libraries( LOCAL_CRATE, link_static, link_dynamic, + link_output_kind, ); } @@ -2543,6 +2569,7 @@ fn add_upstream_rust_crates<'a>( codegen_results: &CodegenResults, crate_type: CrateType, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the @@ -2620,6 +2647,7 @@ fn add_upstream_rust_crates<'a>( cnum, link_static, link_dynamic, + link_output_kind, ); } } @@ -2630,6 +2658,7 @@ fn add_upstream_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { let search_path = OnceCell::new(); for &cnum in &codegen_results.crate_info.used_crates { @@ -2658,6 +2687,7 @@ fn add_upstream_native_libraries( cnum, link_static, link_dynamic, + link_output_kind, ); } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ba4b89c9ea10b..e1915e16e2002 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -596,6 +596,17 @@ impl LinkOutputKind { _ => return None, }) } + + pub fn can_link_dylib(self) -> bool { + match self { + LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false, + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::DynamicDylib + | LinkOutputKind::StaticDylib + | LinkOutputKind::WasiReactorExe => true, + } + } } impl fmt::Display for LinkOutputKind { diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3be4382b0a3aa..da3f4cba214ab 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -58,8 +58,8 @@ Example: `-l static:+whole-archive=mylib`. The kind of library and the modifiers can also be specified in a [`#[link]` attribute][link-attribute]. If the kind is not specified in the `link` -attribute or on the command-line, it will link a dynamic library if available, -otherwise it will use a static library. If the kind is specified on the +attribute or on the command-line, it will link a dynamic library by default, +except when building a static executable. If the kind is specified on the command-line, it will override the kind specified in a `link` attribute. The name used in a `link` attribute may be overridden using the form `-l From adbfd0da6860a29d6c7a2adb315e79c91202f7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Thu, 8 Jun 2023 00:52:53 +0800 Subject: [PATCH 760/806] Fix ICE for while loop with assignment condition with LHS place expr --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 13 +++++++++++-- tests/ui/suggestions/while-let-typo.rs | 2 +- tests/ui/suggestions/while-let-typo.stderr | 13 ++++++++++++- ...issue-112385-while-assign-lhs-place-expr-ice.rs | 9 +++++++++ ...e-112385-while-assign-lhs-place-expr-ice.stderr | 14 ++++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs create mode 100644 tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index eba5c829e396d..30a543aab508f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1640,7 +1640,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Stmt { kind: hir::StmtKind::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), + kind: hir::ExprKind::Assign(lhs, ..), .. }), .. @@ -1650,7 +1650,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } = blk { self.comes_from_while_condition(blk.hir_id, |_| { - err.downgrade_to_delayed_bug(); + // We cannot suppress the error if the LHS of assignment + // is a syntactic place expression because E0070 would + // not be emitted by `check_lhs_assignable`. + let res = self.typeck_results.borrow().expr_ty_opt(lhs); + + if !lhs.is_syntactic_place_expr() + || res.references_error() + { + err.downgrade_to_delayed_bug(); + } }) } } diff --git a/tests/ui/suggestions/while-let-typo.rs b/tests/ui/suggestions/while-let-typo.rs index dbbcdee3c19f6..21b254054bbc0 100644 --- a/tests/ui/suggestions/while-let-typo.rs +++ b/tests/ui/suggestions/while-let-typo.rs @@ -2,7 +2,7 @@ fn main() { let foo = Some(0); let bar = None; while Some(x) = foo {} //~ ERROR cannot find value `x` in this scope - while Some(foo) = bar {} + while Some(foo) = bar {} //~ ERROR mismatched types while 3 = foo {} //~ ERROR mismatched types while Some(3) = foo {} //~ ERROR invalid left-hand side of assignment while x = 5 {} //~ ERROR cannot find value `x` in this scope diff --git a/tests/ui/suggestions/while-let-typo.stderr b/tests/ui/suggestions/while-let-typo.stderr index 7cc2ed3149b14..69a7e5761d421 100644 --- a/tests/ui/suggestions/while-let-typo.stderr +++ b/tests/ui/suggestions/while-let-typo.stderr @@ -20,6 +20,17 @@ help: you might have meant to use pattern matching LL | while let x = 5 {} | +++ +error[E0308]: mismatched types + --> $DIR/while-let-typo.rs:5:11 + | +LL | while Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | while let Some(foo) = bar {} + | +++ + error[E0308]: mismatched types --> $DIR/while-let-typo.rs:6:11 | @@ -39,7 +50,7 @@ help: you might have meant to use pattern destructuring LL | while let Some(3) = foo {} | +++ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0070, E0308, E0425. For more information about an error, try `rustc --explain E0070`. diff --git a/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs new file mode 100644 index 0000000000000..3cb011dc06fcd --- /dev/null +++ b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs @@ -0,0 +1,9 @@ +// Previously, the while loop with an assignment statement (mistakenly) as the condition +// which has a place expr as the LHS would trigger an ICE in typeck. +// Reduced from https://github.com/rust-lang/rust/issues/112385. + +fn main() { + let foo = Some(()); + while Some(foo) = None {} + //~^ ERROR mismatched types +} diff --git a/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr new file mode 100644 index 0000000000000..cf2648d084079 --- /dev/null +++ b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/issue-112385-while-assign-lhs-place-expr-ice.rs:7:11 + | +LL | while Some(foo) = None {} + | ^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | while let Some(foo) = None {} + | +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From fbe3a475f290c0847623357c732caa62b29389f3 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 7 Jun 2023 18:59:31 +0000 Subject: [PATCH 761/806] Don't `use compile_error as print` --- compiler/rustc_driver_impl/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 14888cf4d75c0..c503a44c19698 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -58,11 +58,18 @@ use std::str; use std::sync::OnceLock; use std::time::Instant; +#[allow(unused_macros)] +macro do_not_use_print($($t:tt)*) { + std::compile_error!( + "Don't use `print` or `println` here, use `safe_print` or `safe_println` instead" + ) +} + // This import blocks the use of panicking `print` and `println` in all the code // below. Please use `safe_print` and `safe_println` to avoid ICE when // encountering an I/O error during print. #[allow(unused_imports)] -use std::{compile_error as print, compile_error as println}; +use {do_not_use_print as print, do_not_use_print as println}; pub mod args; pub mod pretty; From c38d80ee9f4fde1596ae4bff0733c3ec045e2f3e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 7 Jun 2023 17:17:27 +0000 Subject: [PATCH 762/806] Track more diagnostics in `rustc_expand` --- compiler/rustc_expand/src/base.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0d43b30474b06..6839e6ffa3511 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1108,6 +1108,7 @@ impl<'a> ExtCtxt<'a> { } #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_span_err>( &self, sp: S, @@ -1116,6 +1117,7 @@ impl<'a> ExtCtxt<'a> { self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg) } + #[track_caller] pub fn create_err( &self, err: impl IntoDiagnostic<'a>, @@ -1123,6 +1125,7 @@ impl<'a> ExtCtxt<'a> { self.sess.create_err(err) } + #[track_caller] pub fn emit_err(&self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { self.sess.emit_err(err) } @@ -1133,10 +1136,12 @@ impl<'a> ExtCtxt<'a> { /// Compilation will be stopped in the near future (at the end of /// the macro expansion phase). #[rustc_lint_diagnostics] + #[track_caller] pub fn span_err>(&self, sp: S, msg: impl Into) { self.sess.parse_sess.span_diagnostic.span_err(sp, msg); } #[rustc_lint_diagnostics] + #[track_caller] pub fn span_warn>(&self, sp: S, msg: impl Into) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } From 8efcb28d3c3b804544e9a0a990b8abff6705a2bc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 8 Jun 2023 03:21:13 +0000 Subject: [PATCH 763/806] Do fix_*_builtin_expr hacks on the writeback results --- compiler/rustc_hir_typeck/src/writeback.rs | 83 ++++++++----------- .../normalized-const-built-in-op.rs | 11 +++ 2 files changed, 46 insertions(+), 48 deletions(-) create mode 100644 tests/ui/traits/new-solver/normalized-const-built-in-op.rs diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6a3a46c778a7a..a395858262f04 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -14,7 +14,6 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -148,31 +147,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr<'_>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, inner) => { - let inner_ty = self.fcx.node_ty(inner.hir_id); - let inner_ty = self.fcx.resolve_vars_if_possible(inner_ty); + let inner_ty = self.typeck_results.node_type(inner.hir_id); if inner_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); } } hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => { - let lhs_ty = self.fcx.node_ty(lhs.hir_id); - let lhs_ty = self.fcx.resolve_vars_if_possible(lhs_ty); - - let rhs_ty = self.fcx.node_ty(rhs.hir_id); - let rhs_ty = self.fcx.resolve_vars_if_possible(rhs_ty); + let lhs_ty = self.typeck_results.node_type(lhs.hir_id); + let rhs_ty = self.typeck_results.node_type(rhs.hir_id); if lhs_ty.is_scalar() && rhs_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); match e.kind { hir::ExprKind::Binary(..) => { if !op.node.is_by_value() { - let mut adjustments = typeck_results.adjustments_mut(); + let mut adjustments = self.typeck_results.adjustments_mut(); if let Some(a) = adjustments.get_mut(lhs.hir_id) { a.pop(); } @@ -182,7 +175,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } hir::ExprKind::AssignOp(..) - if let Some(a) = typeck_results.adjustments_mut().get_mut(lhs.hir_id) => + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) => { a.pop(); } @@ -200,16 +193,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // if they are not we don't modify the expr, hence we bypass the ICE fn is_builtin_index( &mut self, - typeck_results: &TypeckResults<'tcx>, e: &hir::Expr<'_>, base_ty: Ty<'tcx>, index_ty: Ty<'tcx>, ) -> bool { - if let Some(elem_ty) = base_ty.builtin_index() { - let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; - let resolved_exp_ty = self.resolve(exp_ty, &e.span); - - elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize + if let Some(elem_ty) = base_ty.builtin_index() + && let Some(exp_ty) = self.typeck_results.expr_ty_opt(e) + { + elem_ty == exp_ty && index_ty == self.fcx.tcx.types.usize } else { false } @@ -221,38 +212,34 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { if let hir::ExprKind::Index(ref base, ref index) = e.kind { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = typeck_results - .expr_ty_adjusted_opt(base) - .map(|t| self.fcx.resolve_vars_if_possible(t).kind()); + let base_ty = self.typeck_results.expr_ty_adjusted_opt(base); if base_ty.is_none() { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been // emitted, so we delay an ICE if none have. (#64638) self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base)); } - if let Some(ty::Ref(_, base_ty, _)) = base_ty { - let index_ty = typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { - // When encountering `return [0][0]` outside of a `fn` body we would attempt - // to access an nonexistent index. We assume that more relevant errors will - // already have been emitted, so we only gate on this with an ICE if no - // error has been emitted. (#64638) - self.fcx.tcx.ty_error_with_message( - e.span, - format!("bad index {:?} for base: `{:?}`", index, base), - ) - }); - let index_ty = self.fcx.resolve_vars_if_possible(index_ty); - let resolved_base_ty = self.resolve(*base_ty, &base.span); - - if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { + if let Some(base_ty) = base_ty + && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() + { + let index_ty = + self.typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { + // When encountering `return [0][0]` outside of a `fn` body we would attempt + // to access an nonexistent index. We assume that more relevant errors will + // already have been emitted, so we only gate on this with an ICE if no + // error has been emitted. (#64638) + self.fcx.tcx.ty_error_with_message( + e.span, + format!("bad index {:?} for base: `{:?}`", index, base), + ) + }); + if self.is_builtin_index(e, base_ty_inner, index_ty) { // Remove the method call record - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); - if let Some(a) = typeck_results.adjustments_mut().get_mut(base.hir_id) { + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) { // Discard the need for a mutable borrow // Extra adjustment made when indexing causes a drop @@ -283,9 +270,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.fix_scalar_builtin_expr(e); - self.fix_index_builtin_expr(e); - match e.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = self.fcx.tcx.hir().body(body); @@ -314,6 +298,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.visit_node_id(e.span, e.hir_id); intravisit::walk_expr(self, e); + + self.fix_scalar_builtin_expr(e); + self.fix_index_builtin_expr(e); } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { diff --git a/tests/ui/traits/new-solver/normalized-const-built-in-op.rs b/tests/ui/traits/new-solver/normalized-const-built-in-op.rs new file mode 100644 index 0000000000000..2443e51781391 --- /dev/null +++ b/tests/ui/traits/new-solver/normalized-const-built-in-op.rs @@ -0,0 +1,11 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +const fn foo() { + let mut x = [1, 2, 3]; + // We need to fix up `<<[i32; 3] as Index>::Output as AddAssign>` + // to be treated like a built-in operation. + x[1] += 5; +} + +fn main() {} From 54fb5a48b968b3a329ceeb57226d9ac60f983f04 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 8 Jun 2023 04:19:27 +0000 Subject: [PATCH 764/806] Structurally resolve correctly in check_pat_lit --- compiler/rustc_hir_typeck/src/pat.rs | 5 ++--- tests/ui/traits/new-solver/slice-match-byte-lit.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/ui/traits/new-solver/slice-match-byte-lit.rs diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5af955d313482..2f9871a103a83 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -393,9 +393,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // They can denote both statically and dynamically-sized byte arrays. let mut pat_ty = ty; if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { - let expected = self.structurally_resolved_type(span, expected); - if let ty::Ref(_, inner_ty, _) = expected.kind() - && matches!(inner_ty.kind(), ty::Slice(_)) + if let ty::Ref(_, inner_ty, _) = *self.structurally_resolved_type(span, expected).kind() + && self.structurally_resolved_type(span, inner_ty).is_slice() { let tcx = self.tcx; trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); diff --git a/tests/ui/traits/new-solver/slice-match-byte-lit.rs b/tests/ui/traits/new-solver/slice-match-byte-lit.rs new file mode 100644 index 0000000000000..4f848062595da --- /dev/null +++ b/tests/ui/traits/new-solver/slice-match-byte-lit.rs @@ -0,0 +1,11 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +fn test(s: &[u8]) { + match &s[0..3] { + b"uwu" => {} + _ => {} + } +} + +fn main() {} From b512004a4a04f80c650cde6b2239cd41c5509fc6 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 7 Jun 2023 21:27:51 -0700 Subject: [PATCH 765/806] Fix typo Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- library/core/src/any.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 89c613aef9971..09f52d692d0aa 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -713,7 +713,7 @@ impl hash::Hash for TypeId { // because: // - The hashing algorithm which backs `TypeId` is expected to be // unbiased and high quality, meaning further mixing would be somewhat - // redundant compared to choosing (the lower) 64 bits arbitrarially. + // redundant compared to choosing (the lower) 64 bits arbitrarily. // - `Hasher::finish` returns a u64 anyway, so the extra entropy we'd // get from hashing the full value would probably not be useful // (especially given the previous point about the lower 64 bits being From 54d7b327e5182b97fcdb8d90bf7853ffe54364c3 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Thu, 8 Jun 2023 00:38:50 -0400 Subject: [PATCH 766/806] Removed stable/unstable sort arg from into_sorted_stable_ord, fixed a few misc issues, added collect to UnordItems --- compiler/rustc_abi/src/lib.rs | 4 +- .../src/stable_hasher.rs | 45 +++++++++++++++---- compiler/rustc_data_structures/src/unord.rs | 8 +++- compiler/rustc_hir/src/hir_id.rs | 4 +- .../src/assert_module_sources.rs | 2 +- .../src/persist/dirty_clean.rs | 8 ++-- compiler/rustc_incremental/src/persist/fs.rs | 15 +++---- .../rustc_incremental/src/persist/fs/tests.rs | 13 ++---- .../src/persist/work_product.rs | 7 +-- compiler/rustc_interface/src/queries.rs | 11 +++-- .../src/dep_graph/dep_node.rs | 6 ++- compiler/rustc_session/src/config.rs | 4 +- .../clippy_lints/src/wildcard_imports.rs | 2 +- 13 files changed, 81 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 2ee63c286ba5c..e1b9987f57816 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -414,7 +414,9 @@ pub struct Size { // Safety: Ord is implement as just comparing numerical values and numerical values // are not changed by (de-)serialization. #[cfg(feature = "nightly")] -unsafe impl StableOrd for Size {} +unsafe impl StableOrd for Size { + const CAN_USE_UNSTABLE_SORT: bool = true; +} // This is debug-printed a lot in larger structs, don't waste too much space there impl fmt::Debug for Size { diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index a895e28c822cb..0c1fb7518fa3b 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -233,7 +233,17 @@ pub trait ToStableHashKey { /// - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete /// values depend on state that might be different between /// compilation sessions. -pub unsafe trait StableOrd: Ord {} +/// +/// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether +/// unstable sorting can be used for this type. Set to true if and +/// only if `a == b` implies `a` and `b` are fully indistinguishable. +pub unsafe trait StableOrd: Ord { + const CAN_USE_UNSTABLE_SORT: bool; +} + +unsafe impl StableOrd for &T { + const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; +} /// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since /// that has the same requirements. @@ -253,7 +263,9 @@ macro_rules! impl_stable_traits_for_trivial_type { } } - unsafe impl $crate::stable_hasher::StableOrd for $t {} + unsafe impl $crate::stable_hasher::StableOrd for $t { + const CAN_USE_UNSTABLE_SORT: bool = true; + } }; } @@ -339,7 +351,9 @@ impl, T2: HashStable, CTX> HashStable for (T1, T2) } } -unsafe impl StableOrd for (T1, T2) {} +unsafe impl StableOrd for (T1, T2) { + const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT; +} impl HashStable for (T1, T2, T3) where @@ -355,7 +369,10 @@ where } } -unsafe impl StableOrd for (T1, T2, T3) {} +unsafe impl StableOrd for (T1, T2, T3) { + const CAN_USE_UNSTABLE_SORT: bool = + T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT; +} impl HashStable for (T1, T2, T3, T4) where @@ -376,6 +393,10 @@ where unsafe impl StableOrd for (T1, T2, T3, T4) { + const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT + && T2::CAN_USE_UNSTABLE_SORT + && T3::CAN_USE_UNSTABLE_SORT + && T4::CAN_USE_UNSTABLE_SORT; } impl, CTX> HashStable for [T] { @@ -468,7 +489,9 @@ impl HashStable for str { } } -unsafe impl StableOrd for &str {} +unsafe impl StableOrd for &str { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl HashStable for String { #[inline] @@ -479,7 +502,9 @@ impl HashStable for String { // Safety: String comparison only depends on their contents and the // contents are not changed by (de-)serialization. -unsafe impl StableOrd for String {} +unsafe impl StableOrd for String { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl ToStableHashKey for String { type KeyType = String; @@ -505,7 +530,9 @@ impl HashStable for bool { } // Safety: sort order of bools is not changed by (de-)serialization. -unsafe impl StableOrd for bool {} +unsafe impl StableOrd for bool { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl HashStable for Option where @@ -523,7 +550,9 @@ where } // Safety: the Option wrapper does not add instability to comparison. -unsafe impl StableOrd for Option {} +unsafe impl StableOrd for Option { + const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; +} impl HashStable for Result where diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 6c8d541463158..e18c7b415f6cf 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -140,12 +140,12 @@ impl> UnordItems { } #[inline] - pub fn into_sorted_stable_ord(self, use_stable_sort: bool) -> Vec + pub fn into_sorted_stable_ord(self) -> Vec where T: Ord + StableOrd, { let mut items: Vec = self.0.collect(); - if use_stable_sort { + if !T::CAN_USE_UNSTABLE_SORT { items.sort(); } else { items.sort_unstable() @@ -161,6 +161,10 @@ impl> UnordItems { items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx)); items } + + pub fn collect>>(self) -> C { + self.into() + } } /// This is a set collection type that tries very hard to not expose diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index d549f52f873a9..34c6157793663 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -166,7 +166,9 @@ impl ItemLocalId { // Safety: Ord is implement as just comparing the ItemLocalId's numerical // values and these are not changed by (de-)serialization. -unsafe impl StableOrd for ItemLocalId {} +unsafe impl StableOrd for ItemLocalId { + const CAN_USE_UNSTABLE_SORT: bool = true; +} /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. pub const CRATE_HIR_ID: HirId = diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 3d9b1c2e7411f..0111a6d302d4a 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -119,7 +119,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !self.available_cgus.contains(&cgu_name) { let cgu_names: Vec<&str> = - self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(true); + self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); self.tcx.sess.emit_err(errors::NoModuleNamed { span: attr.span, user_path, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 786a0e0d3b230..cbe77e7b16de8 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -198,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let (name, mut auto) = self.auto_labels(item_id, attr); let except = self.except(attr); let loaded_from_disk = self.loaded_from_disk(attr); - for e in except.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { + for e in except.items().map(|x| x.as_str()).into_sorted_stable_ord() { if !auto.remove(e) { self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } @@ -377,16 +377,16 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - for label in assertion.clean.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { + for label in assertion.clean.items().map(|x| x.as_str()).into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); } - for label in assertion.dirty.items().map(|x| x.as_str()).into_sorted_stable_ord(false) { + for label in assertion.dirty.items().map(|x| x.as_str()).into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); } for label in - assertion.loaded_from_disk.items().map(|x| x.as_str()).into_sorted_stable_ord(false) + assertion.loaded_from_disk.items().map(|x| x.as_str()).into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 550772a688121..243057b99bca2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -676,11 +676,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - let lock_file_to_session_dir_iter = lock_file_to_session_dir - .items() - .map(|(file, dir)| (file.as_str(), dir.as_ref().map(|y| y.as_str()))); for (lock_file_name, directory_name) in - lock_file_to_session_dir_iter.into_sorted_stable_ord(false) + lock_file_to_session_dir.items().into_sorted_stable_ord() { if directory_name.is_none() { let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { @@ -712,10 +709,10 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Filter out `None` directories - let lock_file_to_session_dir: UnordMap = - UnordMap::from(lock_file_to_session_dir.into_items().filter_map( - |(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n)), - )); + let lock_file_to_session_dir: UnordMap = lock_file_to_session_dir + .into_items() + .filter_map(|(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n))) + .into(); // Delete all session directories that don't have a lock file. for directory_name in session_directories { @@ -821,7 +818,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } None }); - let deletion_candidates = UnordMap::from(deletion_candidates); + let deletion_candidates = deletion_candidates.into(); // Delete all but the most recent of the candidates all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 4c1cb5725dc9c..644b8187621c9 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -2,23 +2,16 @@ use super::*; #[test] fn test_all_except_most_recent() { - let computed: UnordMap<_, Option> = UnordMap::from_iter([ + let input: UnordMap<_, Option> = UnordMap::from_iter([ ((UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4")), None), ((UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1")), None), ((UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5")), None), ((UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3")), None), ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None), ]); - let mut paths = UnordSet::default(); - paths.extend_unord(all_except_most_recent(computed).into_items().map(|(path, _)| path)); assert_eq!( - UnordSet::from(paths), - UnordSet::from_iter([ - PathBuf::from("1"), - PathBuf::from("2"), - PathBuf::from("3"), - PathBuf::from("4") - ]) + all_except_most_recent(input).into_items().map(|(path, _)| path).into_sorted_stable_ord(), + vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4")] ); assert!(all_except_most_recent(UnordMap::default()).is_empty()); diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index ae604b2ca0be7..bce5ca1e16bd1 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -46,12 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - for path in work_product - .saved_files - .items() - .map(|(_, path)| path.as_str()) - .into_sorted_stable_ord(false) - { + for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 6975fbd917a4f..455a8129656d4 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -194,10 +194,15 @@ impl<'tcx> Queries<'tcx> { let future_opt = self.dep_graph_future()?.steal(); let dep_graph = future_opt .and_then(|future| { - let (prev_graph, prev_work_products) = + let (prev_graph, mut prev_work_products) = sess.time("blocked_on_dep_graph_loading", || future.open().open(sess)); - let prev_work_products = - FxIndexMap::from_iter(prev_work_products.into_sorted(&(), false)); + // Convert from UnordMap to FxIndexMap by sorting + let prev_work_product_ids = + prev_work_products.items().map(|x| *x.0).into_sorted_stable_ord(); + let prev_work_products = prev_work_product_ids + .into_iter() + .map(|x| (x, prev_work_products.remove(&x).unwrap())) + .collect::>(); rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products) }) .unwrap_or_else(DepGraph::new_disabled); diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index e0089f2861547..39a4cb1b179b4 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -46,7 +46,7 @@ use super::{DepContext, DepKind, FingerprintStyle}; use crate::ich::StableHashingContext; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_hir::definitions::DefPathHash; use std::fmt; use std::hash::Hash; @@ -254,3 +254,7 @@ impl ToStableHashKey for WorkProductId { self.hash } } +unsafe impl StableOrd for WorkProductId { + // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well + const CAN_USE_UNSTABLE_SORT: bool = true; +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0ce83e7909771..84d9f75532b08 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -311,7 +311,9 @@ pub enum OutputType { } // Safety: Trivial C-Style enums have a stable sort order across compilation sessions. -unsafe impl StableOrd for OutputType {} +unsafe impl StableOrd for OutputType { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl ToStableHashKey for OutputType { type KeyType = Self; diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index b6e4cd22789f9..2a3d86988bb04 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -160,7 +160,7 @@ impl LateLintPass<'_> for WildcardImports { ) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false); + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { From 4ae250bf4e285cbd0a474207e8e3fc8d2c5ed9ee Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Thu, 8 Jun 2023 00:40:29 -0400 Subject: [PATCH 767/806] fixup! Removed stable/unstable sort arg from into_sorted_stable_ord, fixed a few misc issues, added collect to UnordItems --- src/tools/clippy/clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 2a3d86988bb04..b6e4cd22789f9 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -160,7 +160,7 @@ impl LateLintPass<'_> for WildcardImports { ) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { From 3f324a8b7d62f44f97ba76eeb76b67d6ff0bf744 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Thu, 8 Jun 2023 01:05:38 -0400 Subject: [PATCH 768/806] Whoops, submodule change was actually valid - undoing fixup --- src/tools/clippy/clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index b6e4cd22789f9..2a3d86988bb04 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -160,7 +160,7 @@ impl LateLintPass<'_> for WildcardImports { ) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false); + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { From 400fad779efec3dbce7746d7d38438c3bf394d3a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 7 Jun 2023 22:26:31 -0700 Subject: [PATCH 769/806] add programmerjake to portable-simd cc list --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 42190061ef90d..0f0b31e9f38a1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -378,7 +378,7 @@ Portable SIMD is developed in its own repository. If possible, consider \ making this change to [rust-lang/portable-simd](https://github.com/rust-lang/portable-simd) \ instead. """ -cc = ["@calebzulawski"] +cc = ["@calebzulawski", "@programmerjake"] [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"] From 5e57e27d7a949f56402b80e2a78c618ed8a3aedb Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 8 Jun 2023 00:57:22 +0900 Subject: [PATCH 770/806] add a test for #105709 replace build with check Co-authored-by: Michael Goulet use appropriate test name --- .../inline-const-in-const-generic-defaults.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs diff --git a/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs b/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs new file mode 100644 index 0000000000000..d81cba6275405 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![feature(inline_const)] +#![allow(incomplete_features)] + +pub struct ConstDefaultUnstable; + +pub fn main() {} From 29a51e14d9d7eb299683556a172cd06a11237b35 Mon Sep 17 00:00:00 2001 From: sladynnunes Date: Sun, 28 May 2023 02:14:36 -0700 Subject: [PATCH 771/806] Migrate item_opaque_type to Askama Migrate item_opaque_type to Askama Fix wrap_item parameters Fix to write --- src/librustdoc/html/render/print_item.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 62027a3fa1941..1e36ca1ae7f5d 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1124,7 +1124,12 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) } -fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { +fn item_opaque_ty( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::OpaqueTy, +) { wrap_item(w, |w| { write!( w, @@ -1134,16 +1139,18 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + .unwrap(); } fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { From 52300bf8d859a81ce73b13e87deb408473a83324 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 12 May 2023 19:30:15 +0200 Subject: [PATCH 772/806] Uplift clippy::undropped_manually_drops to rustc --- compiler/rustc_lint/messages.ftl | 4 ++ .../rustc_lint/src/drop_forget_useless.rs | 44 ++++++++++++++++++- compiler/rustc_lint/src/lints.rs | 19 ++++++++ tests/ui/lint/undropped_manually_drops.rs | 19 ++++++++ tests/ui/lint/undropped_manually_drops.stderr | 42 ++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 tests/ui/lint/undropped_manually_drops.rs create mode 100644 tests/ui/lint/undropped_manually_drops.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 98fe3821947d5..51eb8210d46d5 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -492,6 +492,10 @@ lint_tykind = usage of `ty::TyKind` lint_tykind_kind = usage of `ty::TyKind::` .suggestion = try using `ty::` directly +lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + .label = argument has type `{$arg_ty}` + .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value + lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op .label = this function will not propagate the caller location diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index ed2b384805e05..4cea6169dc3d9 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -1,8 +1,12 @@ use rustc_hir::{Arm, Expr, ExprKind, Node}; +use rustc_middle::ty; use rustc_span::sym; use crate::{ - lints::{DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag}, + lints::{ + DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag, UndroppedManuallyDropsDiag, + UndroppedManuallyDropsSuggestion, + }, LateContext, LateLintPass, LintContext, }; @@ -109,7 +113,29 @@ declare_lint! { "calls to `std::mem::forget` with a value that implements Copy" } -declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]); +declare_lint! { + /// The `undropped_manually_drops` lint check for calls to `std::mem::drop` with + /// a value of `std::mem::ManuallyDrop` which doesn't drop. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `ManuallyDrop` does not drop it's inner value so calling `std::mem::drop` will + /// not drop the inner value of the `ManuallyDrop` either. + pub UNDROPPED_MANUALLY_DROPS, + Deny, + "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value" +} + +declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES, UNDROPPED_MANUALLY_DROPS]); impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -134,6 +160,20 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { sym::mem_forget if is_copy => { cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span }); } + sym::mem_drop if let ty::Adt(adt, _) = arg_ty.kind() && adt.is_manually_drop() => { + cx.emit_spanned_lint( + UNDROPPED_MANUALLY_DROPS, + expr.span, + UndroppedManuallyDropsDiag { + arg_ty, + label: arg.span, + suggestion: UndroppedManuallyDropsSuggestion { + start_span: arg.span.shrink_to_lo(), + end_span: arg.span.shrink_to_hi() + } + } + ); + } _ => return, }; } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index fd15f7952023a..03dbffcc2c435 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -699,6 +699,25 @@ pub struct ForgetCopyDiag<'a> { pub label: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_undropped_manually_drops)] +pub struct UndroppedManuallyDropsDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, + #[subdiagnostic] + pub suggestion: UndroppedManuallyDropsSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct UndroppedManuallyDropsSuggestion { + #[suggestion_part(code = "std::mem::ManuallyDrop::into_inner(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + // invalid_from_utf8.rs #[derive(LintDiagnostic)] pub enum InvalidFromUtf8Diag { diff --git a/tests/ui/lint/undropped_manually_drops.rs b/tests/ui/lint/undropped_manually_drops.rs new file mode 100644 index 0000000000000..7286121a40483 --- /dev/null +++ b/tests/ui/lint/undropped_manually_drops.rs @@ -0,0 +1,19 @@ +// check-fail + +struct S; + +fn main() { + let mut manual1 = std::mem::ManuallyDrop::new(S); + let mut manual2 = std::mem::ManuallyDrop::new(S); + let mut manual3 = std::mem::ManuallyDrop::new(S); + + drop(std::mem::ManuallyDrop::new(S)); //~ ERROR calls to `std::mem::drop` + drop(manual1); //~ ERROR calls to `std::mem::drop` + drop({ manual3 }); //~ ERROR calls to `std::mem::drop` + + // These lines will drop `S` and should be okay. + unsafe { + std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(&mut manual2); + } +} diff --git a/tests/ui/lint/undropped_manually_drops.stderr b/tests/ui/lint/undropped_manually_drops.stderr new file mode 100644 index 0000000000000..156b647ebd3a7 --- /dev/null +++ b/tests/ui/lint/undropped_manually_drops.stderr @@ -0,0 +1,42 @@ +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:10:5 + | +LL | drop(std::mem::ManuallyDrop::new(S)); + | ^^^^^------------------------------^ + | | + | argument has type `ManuallyDrop` + | + = note: `#[deny(undropped_manually_drops)]` on by default +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner(std::mem::ManuallyDrop::new(S))); + | +++++++++++++++++++++++++++++++++++ + + +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:11:5 + | +LL | drop(manual1); + | ^^^^^-------^ + | | + | argument has type `ManuallyDrop` + | +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner(manual1)); + | +++++++++++++++++++++++++++++++++++ + + +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:12:5 + | +LL | drop({ manual3 }); + | ^^^^^-----------^ + | | + | argument has type `ManuallyDrop` + | +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner({ manual3 })); + | +++++++++++++++++++++++++++++++++++ + + +error: aborting due to 3 previous errors + From 5f55f863db0d9d635fcbfacc74982e03b0c1a64a Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 12 May 2023 20:02:01 +0200 Subject: [PATCH 773/806] Drop uplifted clippy::undropped_manually_drops --- .../clippy/clippy_lints/src/declared_lints.rs | 1 - .../clippy_lints/src/drop_forget_ref.rs | 44 +------ .../clippy/clippy_lints/src/renamed_lints.rs | 1 + src/tools/clippy/tests/ui/rename.fixed | 2 + src/tools/clippy/tests/ui/rename.rs | 2 + src/tools/clippy/tests/ui/rename.stderr | 108 +++++++++--------- .../tests/ui/undropped_manually_drops.rs | 26 ----- .../tests/ui/undropped_manually_drops.stderr | 19 --- 8 files changed, 64 insertions(+), 139 deletions(-) delete mode 100644 src/tools/clippy/tests/ui/undropped_manually_drops.rs delete mode 100644 src/tools/clippy/tests/ui/undropped_manually_drops.stderr diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 15ff8be0fd942..6270d26131323 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -136,7 +136,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, crate::drop_forget_ref::FORGET_NON_DROP_INFO, - crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO, crate::duplicate_mod::DUPLICATE_MOD_INFO, crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, crate::empty_drop::EMPTY_DROP_INFO, diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 9c60edb179415..7a4b9a87aeb47 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::get_parent_node; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; @@ -47,35 +47,6 @@ declare_clippy_lint! { "call to `std::mem::forget` with a value which does not implement `Drop`" } -declare_clippy_lint! { - /// ### What it does - /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - /// - /// ### Why is this bad? - /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - /// - /// ### Known problems - /// Does not catch cases if the user binds `std::mem::drop` - /// to a different name and calls it that way. - /// - /// ### Example - /// ```rust - /// struct S; - /// drop(std::mem::ManuallyDrop::new(S)); - /// ``` - /// Use instead: - /// ```rust - /// struct S; - /// unsafe { - /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - /// } - /// ``` - #[clippy::version = "1.49.0"] - pub UNDROPPED_MANUALLY_DROPS, - correctness, - "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" -} - const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ @@ -84,7 +55,6 @@ const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value t declare_lint_pass!(DropForgetRef => [ DROP_NON_DROP, FORGET_NON_DROP, - UNDROPPED_MANUALLY_DROPS ]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { @@ -103,17 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { - span_lint_and_help( - cx, - UNDROPPED_MANUALLY_DROPS, - expr.span, - "the inner value of this ManuallyDrop will not be dropped", - None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", - ); - return; - } + sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.param_env) || is_must_use_func_call(cx, arg) diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 4cb4183002339..f76fa76f0764e 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -50,6 +50,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::panic_params", "non_fmt_panics"), ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::undropped_manually_drops", "undropped_manually_drops"), ("clippy::unknown_clippy_lints", "unknown_lints"), ("clippy::unused_label", "unused_labels"), ]; diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 30f2bfc8c1a9a..954145fc4e64b 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -47,6 +47,7 @@ #![allow(named_arguments_used_positionally)] #![allow(suspicious_double_ref_op)] #![allow(temporary_cstring_as_ptr)] +#![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] #![warn(clippy::almost_complete_range)] @@ -97,6 +98,7 @@ #![warn(non_fmt_panics)] #![warn(named_arguments_used_positionally)] #![warn(temporary_cstring_as_ptr)] +#![warn(undropped_manually_drops)] #![warn(unknown_lints)] #![warn(unused_labels)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 3939914d42241..067a3109afb97 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -47,6 +47,7 @@ #![allow(named_arguments_used_positionally)] #![allow(suspicious_double_ref_op)] #![allow(temporary_cstring_as_ptr)] +#![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] #![warn(clippy::almost_complete_letter_range)] @@ -97,6 +98,7 @@ #![warn(clippy::panic_params)] #![warn(clippy::positional_named_format_parameters)] #![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::undropped_manually_drops)] #![warn(clippy::unknown_clippy_lints)] #![warn(clippy::unused_label)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 7290cf32e5b60..1819d108c5740 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,298 +7,304 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` + --> $DIR/rename.rs:101:9 + | +LL | #![warn(clippy::undropped_manually_drops)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` + error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 50 previous errors +error: aborting due to 51 previous errors diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.rs b/src/tools/clippy/tests/ui/undropped_manually_drops.rs deleted file mode 100644 index f4cfc92e1cd02..0000000000000 --- a/src/tools/clippy/tests/ui/undropped_manually_drops.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![warn(clippy::undropped_manually_drops)] - -struct S; - -fn main() { - let f = std::mem::drop; - let g = std::mem::ManuallyDrop::drop; - let mut manual1 = std::mem::ManuallyDrop::new(S); - let mut manual2 = std::mem::ManuallyDrop::new(S); - let mut manual3 = std::mem::ManuallyDrop::new(S); - let mut manual4 = std::mem::ManuallyDrop::new(S); - - // These lines will not drop `S` and should be linted - drop(std::mem::ManuallyDrop::new(S)); - drop(manual1); - - // FIXME: this line is not linted, though it should be - f(manual2); - - // These lines will drop `S` and should be okay. - unsafe { - std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - std::mem::ManuallyDrop::drop(&mut manual3); - g(&mut manual4); - } -} diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr deleted file mode 100644 index 92611a9b7df4a..0000000000000 --- a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: the inner value of this ManuallyDrop will not be dropped - --> $DIR/undropped_manually_drops.rs:14:5 - | -LL | drop(std::mem::ManuallyDrop::new(S)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop - = note: `-D clippy::undropped-manually-drops` implied by `-D warnings` - -error: the inner value of this ManuallyDrop will not be dropped - --> $DIR/undropped_manually_drops.rs:15:5 - | -LL | drop(manual1); - | ^^^^^^^^^^^^^ - | - = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop - -error: aborting due to 2 previous errors - From d9d1c76ded359089e97b3bb30c5b26ffd5c17396 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 13 May 2023 12:13:37 +0200 Subject: [PATCH 774/806] Allow undropped_manually_drops for some tests --- library/core/tests/manually_drop.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/tests/manually_drop.rs b/library/core/tests/manually_drop.rs index 9eac279733af9..22d72d219a758 100644 --- a/library/core/tests/manually_drop.rs +++ b/library/core/tests/manually_drop.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(undropped_manually_drops))] + use core::mem::ManuallyDrop; #[test] From 70f28a91108644a9db8b35ab594a6f978d736c6d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 29 May 2023 15:05:24 +0000 Subject: [PATCH 775/806] Move tests from `ui/discrim` dir --- .../discriminant-ill-typed.rs} | 0 .../discriminant-ill-typed.stderr} | 16 ++++++++-------- .../discriminant-overflow-2.rs} | 0 .../discriminant-overflow-2.stderr} | 16 ++++++++-------- .../discriminant-overflow.rs} | 0 .../discriminant-overflow.stderr} | 16 ++++++++-------- 6 files changed, 24 insertions(+), 24 deletions(-) rename tests/ui/{discrim/discrim-ill-typed.rs => enum-discriminant/discriminant-ill-typed.rs} (100%) rename tests/ui/{discrim/discrim-ill-typed.stderr => enum-discriminant/discriminant-ill-typed.stderr} (85%) rename tests/ui/{discrim/discrim-overflow-2.rs => enum-discriminant/discriminant-overflow-2.rs} (100%) rename tests/ui/{discrim/discrim-overflow.stderr => enum-discriminant/discriminant-overflow-2.stderr} (82%) rename tests/ui/{discrim/discrim-overflow.rs => enum-discriminant/discriminant-overflow.rs} (100%) rename tests/ui/{discrim/discrim-overflow-2.stderr => enum-discriminant/discriminant-overflow.stderr} (83%) diff --git a/tests/ui/discrim/discrim-ill-typed.rs b/tests/ui/enum-discriminant/discriminant-ill-typed.rs similarity index 100% rename from tests/ui/discrim/discrim-ill-typed.rs rename to tests/ui/enum-discriminant/discriminant-ill-typed.rs diff --git a/tests/ui/discrim/discrim-ill-typed.stderr b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr similarity index 85% rename from tests/ui/discrim/discrim-ill-typed.stderr rename to tests/ui/enum-discriminant/discriminant-ill-typed.stderr index 27f516341ee31..2757145285c79 100644 --- a/tests/ui/discrim/discrim-ill-typed.stderr +++ b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:15:16 + --> $DIR/discriminant-ill-typed.rs:15:16 | LL | OhNo = 0_u8, | ^^^^ expected `i8`, found `u8` @@ -10,7 +10,7 @@ LL | OhNo = 0_i8, | ~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:28:16 + --> $DIR/discriminant-ill-typed.rs:28:16 | LL | OhNo = 0_i8, | ^^^^ expected `u8`, found `i8` @@ -21,7 +21,7 @@ LL | OhNo = 0_u8, | ~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:41:16 + --> $DIR/discriminant-ill-typed.rs:41:16 | LL | OhNo = 0_u16, | ^^^^^ expected `i16`, found `u16` @@ -32,7 +32,7 @@ LL | OhNo = 0_i16, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:54:16 + --> $DIR/discriminant-ill-typed.rs:54:16 | LL | OhNo = 0_i16, | ^^^^^ expected `u16`, found `i16` @@ -43,7 +43,7 @@ LL | OhNo = 0_u16, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:67:16 + --> $DIR/discriminant-ill-typed.rs:67:16 | LL | OhNo = 0_u32, | ^^^^^ expected `i32`, found `u32` @@ -54,7 +54,7 @@ LL | OhNo = 0_i32, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:80:16 + --> $DIR/discriminant-ill-typed.rs:80:16 | LL | OhNo = 0_i32, | ^^^^^ expected `u32`, found `i32` @@ -65,7 +65,7 @@ LL | OhNo = 0_u32, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:93:16 + --> $DIR/discriminant-ill-typed.rs:93:16 | LL | OhNo = 0_u64, | ^^^^^ expected `i64`, found `u64` @@ -76,7 +76,7 @@ LL | OhNo = 0_i64, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:106:16 + --> $DIR/discriminant-ill-typed.rs:106:16 | LL | OhNo = 0_i64, | ^^^^^ expected `u64`, found `i64` diff --git a/tests/ui/discrim/discrim-overflow-2.rs b/tests/ui/enum-discriminant/discriminant-overflow-2.rs similarity index 100% rename from tests/ui/discrim/discrim-overflow-2.rs rename to tests/ui/enum-discriminant/discriminant-overflow-2.rs diff --git a/tests/ui/discrim/discrim-overflow.stderr b/tests/ui/enum-discriminant/discriminant-overflow-2.stderr similarity index 82% rename from tests/ui/discrim/discrim-overflow.stderr rename to tests/ui/enum-discriminant/discriminant-overflow-2.stderr index 1b331bb1b8d98..5f7387c590833 100644 --- a/tests/ui/discrim/discrim-overflow.stderr +++ b/tests/ui/enum-discriminant/discriminant-overflow-2.stderr @@ -1,5 +1,5 @@ error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:11:9 + --> $DIR/discriminant-overflow-2.rs:13:9 | LL | OhNo, | ^^^^ overflowed on value after 127 @@ -7,7 +7,7 @@ LL | OhNo, = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:22:9 + --> $DIR/discriminant-overflow-2.rs:22:9 | LL | OhNo, | ^^^^ overflowed on value after 255 @@ -15,7 +15,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:33:9 + --> $DIR/discriminant-overflow-2.rs:31:9 | LL | OhNo, | ^^^^ overflowed on value after 32767 @@ -23,7 +23,7 @@ LL | OhNo, = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:44:9 + --> $DIR/discriminant-overflow-2.rs:40:9 | LL | OhNo, | ^^^^ overflowed on value after 65535 @@ -31,7 +31,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:56:9 + --> $DIR/discriminant-overflow-2.rs:49:9 | LL | OhNo, | ^^^^ overflowed on value after 2147483647 @@ -39,7 +39,7 @@ LL | OhNo, = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:68:9 + --> $DIR/discriminant-overflow-2.rs:58:9 | LL | OhNo, | ^^^^ overflowed on value after 4294967295 @@ -47,7 +47,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:80:9 + --> $DIR/discriminant-overflow-2.rs:67:9 | LL | OhNo, | ^^^^ overflowed on value after 9223372036854775807 @@ -55,7 +55,7 @@ LL | OhNo, = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:92:9 + --> $DIR/discriminant-overflow-2.rs:76:9 | LL | OhNo, | ^^^^ overflowed on value after 18446744073709551615 diff --git a/tests/ui/discrim/discrim-overflow.rs b/tests/ui/enum-discriminant/discriminant-overflow.rs similarity index 100% rename from tests/ui/discrim/discrim-overflow.rs rename to tests/ui/enum-discriminant/discriminant-overflow.rs diff --git a/tests/ui/discrim/discrim-overflow-2.stderr b/tests/ui/enum-discriminant/discriminant-overflow.stderr similarity index 83% rename from tests/ui/discrim/discrim-overflow-2.stderr rename to tests/ui/enum-discriminant/discriminant-overflow.stderr index 3ca84c6675357..2ecc1839f57de 100644 --- a/tests/ui/discrim/discrim-overflow-2.stderr +++ b/tests/ui/enum-discriminant/discriminant-overflow.stderr @@ -1,5 +1,5 @@ error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:13:9 + --> $DIR/discriminant-overflow.rs:11:9 | LL | OhNo, | ^^^^ overflowed on value after 127 @@ -7,7 +7,7 @@ LL | OhNo, = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:22:9 + --> $DIR/discriminant-overflow.rs:22:9 | LL | OhNo, | ^^^^ overflowed on value after 255 @@ -15,7 +15,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:31:9 + --> $DIR/discriminant-overflow.rs:33:9 | LL | OhNo, | ^^^^ overflowed on value after 32767 @@ -23,7 +23,7 @@ LL | OhNo, = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:40:9 + --> $DIR/discriminant-overflow.rs:44:9 | LL | OhNo, | ^^^^ overflowed on value after 65535 @@ -31,7 +31,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:49:9 + --> $DIR/discriminant-overflow.rs:56:9 | LL | OhNo, | ^^^^ overflowed on value after 2147483647 @@ -39,7 +39,7 @@ LL | OhNo, = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:58:9 + --> $DIR/discriminant-overflow.rs:68:9 | LL | OhNo, | ^^^^ overflowed on value after 4294967295 @@ -47,7 +47,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:67:9 + --> $DIR/discriminant-overflow.rs:80:9 | LL | OhNo, | ^^^^ overflowed on value after 9223372036854775807 @@ -55,7 +55,7 @@ LL | OhNo, = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:76:9 + --> $DIR/discriminant-overflow.rs:92:9 | LL | OhNo, | ^^^^ overflowed on value after 18446744073709551615 From 7b8e8adad2553e30329e24f755bc95b6917e98a0 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 29 May 2023 15:51:49 +0000 Subject: [PATCH 776/806] Bless tidy root entry limit --- src/tools/tidy/src/ui_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 412c6e3120ab2..5c6a94877884d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1898; -const ROOT_ENTRY_LIMIT: usize = 871; +const ROOT_ENTRY_LIMIT: usize = 870; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files From 522ae84e0344669ee64754595dd5f04eed9f0d1c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 May 2023 17:39:12 +0000 Subject: [PATCH 777/806] Suggest type mismatches even when using ref syntax on binding --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 12 +++++++++++- tests/ui/switched-expectations.stderr | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 30a543aab508f..48c3d6f08dee1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1404,7 +1404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type of the place it is referencing, and not some // supertype thereof. let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); + if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { + self.emit_type_mismatch_suggestions( + &mut diag, + init.peel_drop_temps(), + init_ty, + local_ty, + None, + None, + ); + diag.emit(); + } init_ty } else { self.check_expr_coercible_to_type(init, local_ty, None) diff --git a/tests/ui/switched-expectations.stderr b/tests/ui/switched-expectations.stderr index 744d8483bd3de..6e1bbf701d77f 100644 --- a/tests/ui/switched-expectations.stderr +++ b/tests/ui/switched-expectations.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/switched-expectations.rs:3:30 | LL | let ref string: String = var; - | ^^^ expected `String`, found `i32` + | ^^^- help: try using a conversion method: `.to_string()` + | | + | expected `String`, found `i32` error: aborting due to previous error From 5e917a6039fb7894273eccf453c34ad907635920 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Tue, 6 Jun 2023 18:26:38 +0300 Subject: [PATCH 778/806] increase the accuracy of effective visibilities calculation --- compiler/rustc_privacy/src/lib.rs | 46 +++++++++---------- .../src/effective_visibilities.rs | 2 +- .../effective_visibilities_full_priv.rs | 21 +++++++++ .../effective_visibilities_full_priv.stderr | 26 +++++++++++ 4 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 tests/ui/privacy/effective_visibilities_full_priv.rs create mode 100644 tests/ui/privacy/effective_visibilities_full_priv.stderr diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 0197b99c7db19..afd32e38d5b87 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -409,8 +409,8 @@ impl VisibilityLike for ty::Visibility { } } -impl VisibilityLike for Option { - const MAX: Self = Some(EffectiveVisibility::from_vis(ty::Visibility::Public)); +impl VisibilityLike for EffectiveVisibility { + const MAX: Self = EffectiveVisibility::from_vis(ty::Visibility::Public); // Type inference is very smart sometimes. // It can make an impl reachable even some components of its type or trait are unreachable. // E.g. methods of `impl ReachableTrait for ReachableTy { ... }` @@ -422,13 +422,14 @@ impl VisibilityLike for Option { // (which require reaching the `DefId`s in them). const SHALLOW: bool = true; fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self { - if let Some(min) = find.min { - return find - .effective_visibilities - .effective_vis(def_id) - .map(|eff_vis| min.min(*eff_vis, find.tcx)); - } - None + let effective_vis = + find.effective_visibilities.effective_vis(def_id).cloned().unwrap_or_else(|| { + let private_vis = + ty::Visibility::Restricted(find.tcx.parent_module_from_def_id(def_id)); + EffectiveVisibility::from_vis(private_vis) + }); + + effective_vis.min(find.min, find.tcx) } } @@ -766,28 +767,23 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } hir::ItemKind::Impl(ref impl_) => { - if let Some(item_ev) = Option::::of_impl( + let item_ev = EffectiveVisibility::of_impl( item.owner_id.def_id, self.tcx, &self.effective_visibilities, - ) { - self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct); + ); + self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct); - self.reach(item.owner_id.def_id, item_ev) - .generics() - .predicates() - .ty() - .trait_ref(); + self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref(); - for impl_item_ref in impl_.items { - let def_id = impl_item_ref.id.owner_id.def_id; - let nominal_vis = - impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); - self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); + for impl_item_ref in impl_.items { + let def_id = impl_item_ref.id.owner_id.def_id; + let nominal_vis = + impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); + self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); - if let Some(impl_item_ev) = self.get(def_id) { - self.reach(def_id, impl_item_ev).generics().predicates().ty(); - } + if let Some(impl_item_ev) = self.get(def_id) { + self.reach(def_id, impl_item_ev).generics().predicates().ty(); } } } diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 7393bdb388a56..4863c9f479015 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -81,7 +81,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> { def_effective_visibilities: Default::default(), import_effective_visibilities: Default::default(), current_private_vis: Visibility::Restricted(CRATE_DEF_ID), - changed: false, + changed: true, }; visitor.def_effective_visibilities.update_root(); diff --git a/tests/ui/privacy/effective_visibilities_full_priv.rs b/tests/ui/privacy/effective_visibilities_full_priv.rs new file mode 100644 index 0000000000000..cc708917586ff --- /dev/null +++ b/tests/ui/privacy/effective_visibilities_full_priv.rs @@ -0,0 +1,21 @@ +#![feature(rustc_attrs)] +#![allow(private_in_public)] + +struct SemiPriv; + +mod m { + #[rustc_effective_visibility] + struct Priv; + //~^ ERROR Direct: pub(self), Reexported: pub(self), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + //~| ERROR not in the table + + #[rustc_effective_visibility] + pub fn foo() {} //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + + #[rustc_effective_visibility] + impl crate::SemiPriv { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + pub fn f(_: Priv) {} + } +} + +fn main() {} diff --git a/tests/ui/privacy/effective_visibilities_full_priv.stderr b/tests/ui/privacy/effective_visibilities_full_priv.stderr new file mode 100644 index 0000000000000..a856aa20d92c5 --- /dev/null +++ b/tests/ui/privacy/effective_visibilities_full_priv.stderr @@ -0,0 +1,26 @@ +error: Direct: pub(self), Reexported: pub(self), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:8:5 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error: not in the table + --> $DIR/effective_visibilities_full_priv.rs:8:5 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:13:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^ + +error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:16:5 + | +LL | impl crate::SemiPriv { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + From af54d584b29e6afd7069bfdad071c43c0aa135f5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 May 2023 18:47:50 +0000 Subject: [PATCH 779/806] More robust as_ref/as_deref suggestions --- compiler/rustc_hir_typeck/messages.ftl | 2 + compiler/rustc_hir_typeck/src/errors.rs | 15 ++ .../src/fn_ctxt/suggestions.rs | 221 +++++++++++------- compiler/rustc_infer/messages.ftl | 3 - compiler/rustc_infer/src/errors/mod.rs | 24 -- .../src/infer/error_reporting/mod.rs | 1 - .../src/infer/error_reporting/suggest.rs | 27 +-- tests/ui/issues/issue-100605.stderr | 14 +- .../ui/let-else/let-else-ref-bindings.stderr | 16 ++ tests/ui/suggestions/as-ref.rs | 2 + tests/ui/suggestions/as-ref.stderr | 32 ++- tests/ui/typeck/issue-89856.stderr | 2 +- 12 files changed, 207 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index aab432eee571b..4728edd837a6c 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item hir_typeck_convert_to_str = try converting the passed type into a `&str` +hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}` + hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_expected_default_return_type = expected `()` because of default return type diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 4222205c84106..20116fde661c4 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -327,3 +327,18 @@ pub struct CtorIsPrivate { pub span: Span, pub def: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_convert_using_method, + code = "{sugg}", + applicability = "machine-applicable", + style = "verbose" +)] +pub struct SuggestConvertViaMethod<'tcx> { + #[primary_span] + pub span: Span, + pub sugg: &'static str, + pub expected: Ty<'tcx>, + pub found: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index e19b0664461a9..21a52d3eccc5e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,8 @@ use super::FnCtxt; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing}; +use crate::errors::{ + AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod, +}; use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let expr = expr.peel_blocks(); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if let Some((suggestion, msg, applicability, verbose, annotation)) = self.suggest_deref_or_ref(expr, found, expected) { @@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } return true; - } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + } + + if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; - } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + } + + if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = *found.kind() && let Some(sp) = self.tcx.hir().span_if_local(def_id) { @@ -343,97 +351,142 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; - } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { + } + + if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { return true; - } else { - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if !methods.is_empty() { - let mut suggestions = methods.iter() - .filter_map(|conversion_method| { - let receiver_method_ident = expr.method_ident(); - if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name - { - return None // do not suggest code that is already there (#53348) - } + } - let method_call_list = [sym::to_vec, sym::to_string]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) - // If receiver is `.clone()` and found type has one of those methods, - // we guess that the user wants to convert from a slice type (`&[]` or `&str`) - // to an owned type (`Vec` or `String`). These conversions clone internally, - // so we remove the user's `clone` call. - { - vec![( - receiver_method.ident.span, - conversion_method.name.to_string() - )] - } else if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), - ] - } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] - }; - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - return true; - } - } else if let ty::Adt(found_adt, found_substs) = found.kind() - && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) - && let ty::Adt(expected_adt, expected_substs) = expected.kind() - && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) - && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() - && inner_ty.is_str() - { - let ty = found_substs.type_at(0); - let mut peeled = ty; - let mut ref_cnt = 0; - while let ty::Ref(_, inner, _) = peeled.kind() { - peeled = *inner; - ref_cnt += 1; - } - if let ty::Adt(adt, _) = peeled.kind() - && Some(adt.did()) == self.tcx.lang_items().string() - { - let sugg = if ref_cnt == 0 { - ".as_deref()" + if !methods.is_empty() { + let mut suggestions = methods + .iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) + } + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. + { + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] } else { - ".map(|x| x.as_str())" + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] }; - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - fluent::hir_typeck_convert_to_str, - sugg, - Applicability::MachineApplicable, - ); - return true; - } + let struct_pat_shorthand_field = + self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); + } + Some(sugg) + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + return true; + } + } + + if let Some((found_ty_inner, expected_ty_inner, error_tys)) = + self.deconstruct_option_or_result(found, expected) + && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() + { + // Check that given `Result<_, E>`, our expected ty is `Result<_, &E>` + let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected) + }); + // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`, + // but those checks need to be a bit more delicate and the benefit is diminishing. + if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_ref()", + expected, + found, + }); + return true; + } else if let Some((deref_ty, _)) = + self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) + && self.can_eq(self.param_env, deref_ty, peeled) + && error_tys_equate_as_ref + { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_deref()", + expected, + found, + }); + return true; + } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() + && Some(adt.did()) == self.tcx.lang_items().string() + && peeled.is_str() + && error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, found, expected) + }) + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + fluent::hir_typeck_convert_to_str, + ".map(|x| x.as_str())", + Applicability::MachineApplicable, + ); + return true; } } false } + fn deconstruct_option_or_result( + &self, + found_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> { + let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else { + return None; + }; + let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else { + return None; + }; + if self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) + { + Some((found_substs.type_at(0), expected_substs.type_at(0), None)) + } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did()) + { + Some(( + found_substs.type_at(0), + expected_substs.type_at(0), + Some((found_substs.type_at(1), expected_substs.type_at(1))), + )) + } else { + None + } + } + /// When encountering the expected boxed value allocated in the stack, suggest allocating it /// in the heap by calling `Box::new()`. pub(in super::super) fn suggest_boxing_when_appropriate( diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index f44c4a7c1e33c..4d0e770636721 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type infer_ril_introduced_here = `'static` requirement introduced here infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type -infer_sarwa_option = you can convert from `&Option` to `Option<&T>` using `.as_ref()` -infer_sarwa_result = you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` - infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index b1e819e83f19b..7e1fa08f23ae2 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1246,30 +1246,6 @@ pub struct FnConsiderCasting { pub casting: String, } -#[derive(Subdiagnostic)] -pub enum SuggestAsRefWhereAppropriate<'a> { - #[suggestion( - infer_sarwa_option, - code = "{snippet}.as_ref()", - applicability = "machine-applicable" - )] - Option { - #[primary_span] - span: Span, - snippet: &'a str, - }, - #[suggestion( - infer_sarwa_result, - code = "{snippet}.as_ref()", - applicability = "machine-applicable" - )] - Result { - #[primary_span] - span: Span, - snippet: &'a str, - }, -} - #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f8a253c894991..ce5b5882e3b26 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1897,7 +1897,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if should_suggest_fixes { self.suggest_tuple_pattern(cause, &exp_found, diag); - self.suggest_as_ref_where_appropriate(span, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); self.suggest_function_pointers(cause, span, &exp_found, diag); diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index d885d040707e4..1422bdc9ea282 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span}; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, - SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, - SuggestTuplePatternOne, TypeErrorAdditionalDiags, + FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait, + SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, + TypeErrorAdditionalDiags, }; use super::TypeErrCtxt; @@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, - /// suggests it. - pub(super) fn suggest_as_ref_where_appropriate( - &self, - span: Span, - exp_found: &ty::error::ExpectedFound>, - diag: &mut Diagnostic, - ) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found) - { - // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` - let snippet = snippet.trim_start_matches('&'); - let subdiag = match msg { - SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet }, - SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet }, - }; - diag.subdiagnostic(subdiag); - } - } - pub(super) fn suggest_function_pointers( &self, cause: &ObligationCause<'tcx>, diff --git a/tests/ui/issues/issue-100605.stderr b/tests/ui/issues/issue-100605.stderr index be30eef2af48f..e4fc5cb367cfa 100644 --- a/tests/ui/issues/issue-100605.stderr +++ b/tests/ui/issues/issue-100605.stderr @@ -13,10 +13,6 @@ note: function defined here | LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- -help: you can convert from `&Option` to `Option<&T>` using `.as_ref()` - | -LL | takes_option(None.as_ref()); - | ~~~~~~~~~~~~~ help: consider removing the borrow | LL - takes_option(&None); @@ -27,10 +23,8 @@ error[E0308]: mismatched types --> $DIR/issue-100605.rs:8:18 | LL | takes_option(&res); - | ------------ ^^^^ - | | | - | | expected `Option<&String>`, found `&Option` - | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `res.as_ref()` + | ------------ ^^^^ expected `Option<&String>`, found `&Option` + | | | arguments to this function are incorrect | = note: expected enum `Option<&String>` @@ -40,6 +34,10 @@ note: function defined here | LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- +help: try using `.as_ref()` to convert `&Option` to `Option<&String>` + | +LL | takes_option(&res.as_ref()); + | +++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr index ada1805e7256f..7093b5fa9af35 100644 --- a/tests/ui/let-else/let-else-ref-bindings.stderr +++ b/tests/ui/let-else/let-else-ref-bindings.stderr @@ -6,6 +6,10 @@ LL | let Some(ref a): Option<&[u8]> = some else { return }; | = note: expected enum `Option<&[u8]>` found enum `Option>` +help: try using `.as_deref()` to convert `Option>` to `Option<&[u8]>` + | +LL | let Some(ref a): Option<&[u8]> = some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:20:38 @@ -15,6 +19,10 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; | = note: expected enum `Option<&[u8]>` found reference `&Option>` +help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` + | +LL | let Some(ref a): Option<&[u8]> = &some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 @@ -26,6 +34,10 @@ LL | let Some(a): Option<&[u8]> = some else { return }; | = note: expected enum `Option<&[u8]>` found enum `Option>` +help: try using `.as_deref()` to convert `Option>` to `Option<&[u8]>` + | +LL | let Some(a): Option<&[u8]> = some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:27:34 @@ -37,6 +49,10 @@ LL | let Some(a): Option<&[u8]> = &some else { return }; | = note: expected enum `Option<&[u8]>` found reference `&Option>` +help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` + | +LL | let Some(a): Option<&[u8]> = &some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:44:46 diff --git a/tests/ui/suggestions/as-ref.rs b/tests/ui/suggestions/as-ref.rs index a053534418589..0d9790ac22974 100644 --- a/tests/ui/suggestions/as-ref.rs +++ b/tests/ui/suggestions/as-ref.rs @@ -24,4 +24,6 @@ fn main() { let multiple_ref_result = &&Ok(Foo); multiple_ref_result.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] multiple_ref_result.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308] + + let _: Result<&usize, _> = &Ok(42); //~ ERROR mismatched types [E0308] } diff --git a/tests/ui/suggestions/as-ref.stderr b/tests/ui/suggestions/as-ref.stderr index 2147d2d92e389..c5b2bb1260f35 100644 --- a/tests/ui/suggestions/as-ref.stderr +++ b/tests/ui/suggestions/as-ref.stderr @@ -74,14 +74,16 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:13:29 | LL | let y: Option<&usize> = x; - | -------------- ^ - | | | - | | expected `Option<&usize>`, found `&Option` - | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `x.as_ref()` + | -------------- ^ expected `Option<&usize>`, found `&Option` + | | | expected due to this | = note: expected enum `Option<&usize>` found reference `&Option` +help: try using `.as_ref()` to convert `&Option` to `Option<&usize>` + | +LL | let y: Option<&usize> = x.as_ref(); + | +++++++++ error[E0308]: mismatched types --> $DIR/as-ref.rs:15:37 @@ -93,10 +95,10 @@ LL | let y: Result<&usize, &usize> = x; | = note: expected enum `Result<&usize, &usize>` found reference `&Result` -help: you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` +help: try using `.as_ref()` to convert `&Result` to `Result<&usize, &usize>` | LL | let y: Result<&usize, &usize> = x.as_ref(); - | ~~~~~~~~~~ + | +++++++++ error[E0308]: mismatched types --> $DIR/as-ref.rs:19:36 @@ -181,6 +183,22 @@ help: consider using `as_ref` instead LL | multiple_ref_result.as_ref().and_then(|arg| Ok(takes_ref(arg))); | +++++++++ -error: aborting due to 11 previous errors +error[E0308]: mismatched types + --> $DIR/as-ref.rs:28:32 + | +LL | let _: Result<&usize, _> = &Ok(42); + | ----------------- ^^^^^^^ expected `Result<&usize, _>`, found `&Result<{integer}, _>` + | | + | expected due to this + | + = note: expected enum `Result<&usize, _>` + found reference `&Result<{integer}, _>` +help: try using `.as_ref()` to convert `&Result<{integer}, _>` to `Result<&usize, _>` + | +LL - let _: Result<&usize, _> = &Ok(42); +LL + let _: Result<&usize, _> = Ok(42).as_ref(); + | + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-89856.stderr b/tests/ui/typeck/issue-89856.stderr index bd76f17246821..0db3e67ede0d4 100644 --- a/tests/ui/typeck/issue-89856.stderr +++ b/tests/ui/typeck/issue-89856.stderr @@ -13,7 +13,7 @@ note: function defined here | LL | fn take_str_maybe(_: Option<&str>) { } | ^^^^^^^^^^^^^^ --------------- -help: try converting the passed type into a `&str` +help: try using `.as_deref()` to convert `Option` to `Option<&str>` | LL | take_str_maybe(option.as_deref()); | +++++++++++ From acf257e62cbd2011f987ee67b5c5dc02f60da88c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 May 2023 19:48:35 +0000 Subject: [PATCH 780/806] Point at correct exprs for assert_eq type mismatch --- compiler/rustc_hir_typeck/src/demand.rs | 60 +++++++++++++++++-- tests/ui/inference/deref-suggestion.stderr | 13 ++-- .../dont-suggest-try_into-in-macros.stderr | 12 ++-- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index b50630e636b2e..b62f689ec6b89 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,7 +3,7 @@ use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; @@ -91,6 +91,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty); } + /// Really hacky heuristic to remap an `assert_eq!` error to the user + /// expressions provided to the macro. + fn adjust_expr_for_assert_eq_macro( + &self, + found_expr: &mut &'tcx hir::Expr<'tcx>, + expected_expr: &mut Option<&'tcx hir::Expr<'tcx>>, + ) { + let Some(expected_expr) = expected_expr else { return; }; + + if !found_expr.span.eq_ctxt(expected_expr.span) { + return; + } + + if !found_expr + .span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| self.tcx.is_diagnostic_item(sym::assert_eq_macro, def_id)) + { + return; + } + + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(found_path), .. }, + ) = found_expr.kind else { return; }; + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(expected_path), .. }, + ) = expected_expr.kind else { return; }; + + for (path, name, idx, var) in [ + (expected_path, "left_val", 0, expected_expr), + (found_path, "right_val", 1, found_expr), + ] { + if let hir::QPath::Resolved(_, path) = path + && let [segment] = path.segments + && segment.ident.name.as_str() == name + && let Res::Local(hir_id) = path.res + && let Some((_, hir::Node::Expr(match_expr))) = self.tcx.hir().parent_iter(hir_id).nth(2) + && let hir::ExprKind::Match(scrutinee, _, _) = match_expr.kind + && let hir::ExprKind::Tup(exprs) = scrutinee.kind + && let hir::ExprKind::AddrOf(_, _, macro_arg) = exprs[idx].kind + { + *var = macro_arg; + } + } + } + /// Requires that the two types unify, and prints an error message if /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { @@ -156,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_coerce( &self, - expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -177,10 +227,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, - expr: &hir::Expr<'tcx>, + mut expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> (Ty<'tcx>, Option>) { let expected = self.resolve_vars_with_obligations(expected); @@ -190,6 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) => e, }; + self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr); + self.set_tainted_by_errors(self.tcx.sess.delay_span_bug( expr.span, "`TypeError` when attempting coercion but no error emitted", diff --git a/tests/ui/inference/deref-suggestion.stderr b/tests/ui/inference/deref-suggestion.stderr index c58aab4226988..096989db0b4e8 100644 --- a/tests/ui/inference/deref-suggestion.stderr +++ b/tests/ui/inference/deref-suggestion.stderr @@ -84,15 +84,16 @@ LL | fn foo3(_: u32) {} | ^^^^ ------ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:37:5 + --> $DIR/deref-suggestion.rs:37:22 | LL | assert_eq!(3i32, &3i32); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `i32`, found `&i32` - | expected because this is `i32` + | ^^^^^ expected `i32`, found `&i32` + | +help: consider removing the borrow + | +LL - assert_eq!(3i32, &3i32); +LL + assert_eq!(3i32, 3i32); | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:40:17 diff --git a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr index bc6342004f4db..319d866003ba8 100644 --- a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr +++ b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/dont-suggest-try_into-in-macros.rs:2:5 + --> $DIR/dont-suggest-try_into-in-macros.rs:2:23 | LL | assert_eq!(10u64, 10usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `u64`, found `usize` - | expected because this is `u64` + | ^^^^^^^ expected `u64`, found `usize` | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: change the type of the numeric literal from `usize` to `u64` + | +LL | assert_eq!(10u64, 10u64); + | ~~~ error: aborting due to previous error From c92140e83831286c36882fa50cc9edc0ecbbc578 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 May 2023 20:35:10 +0000 Subject: [PATCH 781/806] Don't suggest cyclic associated type constraint --- .../src/infer/error_reporting/note_and_explain.rs | 6 ++++++ .../dont-suggest-cyclic-constraint.rs | 11 +++++++++++ .../dont-suggest-cyclic-constraint.stderr | 12 ++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/ui/associated-types/dont-suggest-cyclic-constraint.rs create mode 100644 tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 9a8fac0fc1be3..a723dc3f079d7 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -374,12 +374,18 @@ impl Trait for X { ) { let tcx = self.tcx; + // Don't suggest constraining a projection to something containing itself + if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) { + return; + } + let msg = || { format!( "consider constraining the associated type `{}` to `{}`", values.expected, values.found ) }; + let body_owner = tcx.hir().get_if_local(body_owner_def_id); let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs new file mode 100644 index 0000000000000..6894f6b6cc4a0 --- /dev/null +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs @@ -0,0 +1,11 @@ +use std::fmt::Debug; + +fn foo(mut iter: I, value: &I::Item) +where + I::Item: Eq + Debug, +{ + debug_assert_eq!(iter.next(), Some(value)); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr new file mode 100644 index 0000000000000..3ecac9c83e571 --- /dev/null +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/dont-suggest-cyclic-constraint.rs:7:35 + | +LL | debug_assert_eq!(iter.next(), Some(value)); + | ^^^^^^^^^^^ expected `Option<::Item>`, found `Option<&::Item>` + | + = note: expected enum `Option<::Item>` + found enum `Option<&::Item>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From a9188226a87618f98ae039682e7616a50bcf6c23 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 May 2023 22:11:39 +0000 Subject: [PATCH 782/806] Peel borrows before suggesting as_ref/as_deref --- compiler/rustc_hir_typeck/src/errors.rs | 7 ++++--- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 16 +++++++++++++++- tests/ui/issues/issue-100605.stderr | 5 +++-- tests/ui/let-else/let-else-ref-bindings.stderr | 10 ++++++---- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 20116fde661c4..5161a366ae7d3 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -329,15 +329,16 @@ pub struct CtorIsPrivate { } #[derive(Subdiagnostic)] -#[suggestion( +#[multipart_suggestion( hir_typeck_convert_using_method, - code = "{sugg}", applicability = "machine-applicable", style = "verbose" )] pub struct SuggestConvertViaMethod<'tcx> { - #[primary_span] + #[suggestion_part(code = "{sugg}")] pub span: Span, + #[suggestion_part(code = "")] + pub borrow_removal_span: Option, pub sugg: &'static str, pub expected: Ty<'tcx>, pub found: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 21a52d3eccc5e..3a4fe334f888a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -413,7 +413,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.deconstruct_option_or_result(found, expected) && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() { - // Check that given `Result<_, E>`, our expected ty is `Result<_, &E>` + // Suggest removing any stray borrows (unless there's macro shenanigans involved). + let inner_expr = expr.peel_borrows(); + if !inner_expr.span.eq_ctxt(expr.span) { + return false; + } + let borrow_removal_span = if inner_expr.hir_id == expr.hir_id { + None + } else { + Some(expr.span.shrink_to_lo().until(inner_expr.span)) + }; + // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for + // `as_ref` and `as_deref` compatibility. let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected) }); @@ -425,6 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg: ".as_ref()", expected, found, + borrow_removal_span, }); return true; } else if let Some((deref_ty, _)) = @@ -437,11 +449,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg: ".as_deref()", expected, found, + borrow_removal_span, }); return true; } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() && Some(adt.did()) == self.tcx.lang_items().string() && peeled.is_str() + // `Result::map`, conversely, does not take ref of the error type. && error_tys.map_or(true, |(found, expected)| { self.can_eq(self.param_env, found, expected) }) diff --git a/tests/ui/issues/issue-100605.stderr b/tests/ui/issues/issue-100605.stderr index e4fc5cb367cfa..6f11f44755a9d 100644 --- a/tests/ui/issues/issue-100605.stderr +++ b/tests/ui/issues/issue-100605.stderr @@ -36,8 +36,9 @@ LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- help: try using `.as_ref()` to convert `&Option` to `Option<&String>` | -LL | takes_option(&res.as_ref()); - | +++++++++ +LL - takes_option(&res); +LL + takes_option(res.as_ref()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr index 7093b5fa9af35..0886d7f1770e9 100644 --- a/tests/ui/let-else/let-else-ref-bindings.stderr +++ b/tests/ui/let-else/let-else-ref-bindings.stderr @@ -21,8 +21,9 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; found reference `&Option>` help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` | -LL | let Some(ref a): Option<&[u8]> = &some.as_deref() else { return }; - | +++++++++++ +LL - let Some(ref a): Option<&[u8]> = &some else { return }; +LL + let Some(ref a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 @@ -51,8 +52,9 @@ LL | let Some(a): Option<&[u8]> = &some else { return }; found reference `&Option>` help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` | -LL | let Some(a): Option<&[u8]> = &some.as_deref() else { return }; - | +++++++++++ +LL - let Some(a): Option<&[u8]> = &some else { return }; +LL + let Some(a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:44:46 From 313143b6a3d3f94b8ab4a4995eaf9935a8dfc7ab Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 6 Jun 2023 10:58:14 -0300 Subject: [PATCH 783/806] Add Terminator::InlineAsm conversion from MIR to SMIR --- compiler/rustc_smir/src/rustc_smir/mod.rs | 35 ++++++++++++++++++- .../rustc_smir/src/stable_mir/mir/body.rs | 17 +++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 478a7db3792da..6d8d99cfb5f1f 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -287,6 +287,27 @@ fn rustc_generator_to_generator( } } +fn rustc_inline_asm_operand_to_inline_asm_operand( + operand: &rustc_middle::mir::InlineAsmOperand<'_>, +) -> stable_mir::mir::InlineAsmOperand { + use rustc_middle::mir::InlineAsmOperand; + + let (in_value, out_place) = match operand { + InlineAsmOperand::In { value, .. } => (Some(rustc_op_to_op(value)), None), + InlineAsmOperand::Out { place, .. } => { + (None, place.map(|place| rustc_place_to_place(&place))) + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + (Some(rustc_op_to_op(in_value)), out_place.map(|place| rustc_place_to_place(&place))) + } + InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::SymStatic { .. } => (None, None), + }; + + stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{:?}", operand) } +} + fn rustc_terminator_to_terminator( terminator: &rustc_middle::mir::Terminator<'_>, ) -> stable_mir::mir::Terminator { @@ -330,7 +351,19 @@ fn rustc_terminator_to_terminator( target: target.as_usize(), unwind: rustc_unwind_to_unwind(unwind), }, - InlineAsm { .. } => todo!(), + InlineAsm { template, operands, options, line_spans, destination, unwind } => { + Terminator::InlineAsm { + template: format!("{:?}", template), + operands: operands + .iter() + .map(|operand| rustc_inline_asm_operand_to_inline_asm_operand(operand)) + .collect(), + options: format!("{:?}", options), + line_spans: format!("{:?}", line_spans), + destination: destination.map(|d| d.as_usize()), + unwind: rustc_unwind_to_unwind(unwind), + } + } Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(), } } diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index 6328c35aa5982..9df7b4945b70a 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -46,6 +46,23 @@ pub enum Terminator { unwind: UnwindAction, }, GeneratorDrop, + InlineAsm { + template: String, + operands: Vec, + options: String, + line_spans: String, + destination: Option, + unwind: UnwindAction, + }, +} + +#[derive(Clone, Debug)] +pub struct InlineAsmOperand { + pub in_value: Option, + pub out_place: Option, + // This field has a raw debug representation of MIR's InlineAsmOperand. + // For now we care about place/operand + the rest in a debug format. + pub raw_rpr: String, } #[derive(Clone, Debug)] From d74ec96e8d334c765e35707f7b7f3c6499a1b43c Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 2 Jun 2023 18:51:27 +0200 Subject: [PATCH 784/806] Move float breaking out of Parser::parse_expr_tuple_field_access_float Purely a refactor in preparation of using it in offset_of!() parsing --- compiler/rustc_parse/src/parser/expr.rs | 75 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1b28f3c97e8f6..9e5f85dc7dc0a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -91,6 +91,18 @@ impl From> for LhsExpr { } } +#[derive(Debug)] +enum DestructuredFloat { + /// 1e2 + Single(Symbol, Span), + /// 1. + TrailingDot(Symbol, Span, Span), + /// 1.2 | 1.2e3 + MiddleDot(Symbol, Span, Span, Symbol, Span), + /// Invalid + Error, +} + impl<'a> Parser<'a> { /// Parses an expression. #[inline] @@ -1013,13 +1025,8 @@ impl<'a> Parser<'a> { // support pushing "future tokens" (would be also helpful to `break_and_eat`), or // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). - fn parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P, - float: Symbol, - suffix: Option, - ) -> P { + // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`. + fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), @@ -1056,7 +1063,7 @@ impl<'a> Parser<'a> { match &*components { // 1e2 [IdentLike(i)] => { - self.parse_expr_tuple_field_access(lo, base, Symbol::intern(&i), suffix, None) + DestructuredFloat::Single(Symbol::intern(&i), span) } // 1. [IdentLike(i), Punct('.')] => { @@ -1068,11 +1075,8 @@ impl<'a> Parser<'a> { } else { (span, span) }; - assert!(suffix.is_none()); let symbol = Symbol::intern(&i); - self.token = Token::new(token::Ident(symbol, false), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, symbol, None, Some(next_token)) + DestructuredFloat::TrailingDot(symbol, ident_span, dot_span) } // 1.2 | 1.2e3 [IdentLike(i1), Punct('.'), IdentLike(i2)] => { @@ -1088,16 +1092,8 @@ impl<'a> Parser<'a> { (span, span, span) }; let symbol1 = Symbol::intern(&i1); - self.token = Token::new(token::Ident(symbol1, false), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); - let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) } // 1e+ | 1e- (recovered) [IdentLike(_), Punct('+' | '-')] | @@ -1109,12 +1105,47 @@ impl<'a> Parser<'a> { [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. self.error_unexpected_after_dot(); - base + DestructuredFloat::Error } _ => panic!("unexpected components in a float token: {:?}", components), } } + fn parse_expr_tuple_field_access_float( + &mut self, + lo: Span, + base: P, + float: Symbol, + suffix: Option, + ) -> P { + match self.break_up_float(float) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, false), ident_span); + let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); + self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { + self.token = Token::new(token::Ident(symbol1, false), ident1_span); + // This needs to be `Spacing::Alone` to prevent regressions. + // See issue #76399 and PR #76285 for more details + let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); + let base1 = + self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); + let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); + self.bump_with((next_token2, self.token_spacing)); // `.` + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + } + DestructuredFloat::Error => base, + } + } + fn parse_expr_tuple_field_access( &mut self, lo: Span, From 1b90f5efaf46073a7da509a895a0688e1c6300c3 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 2 Jun 2023 18:53:04 +0200 Subject: [PATCH 785/806] Support float-like tuple indices in offset_of!() The tokenizer gives us whole float literal tokens, we have to split them up in order to be able to create field access from them. --- compiler/rustc_parse/src/parser/expr.rs | 47 +++- tests/ui/offset-of/offset-of-tuple-nested.rs | 32 +++ tests/ui/offset-of/offset-of-tuple.rs | 52 ++++- tests/ui/offset-of/offset-of-tuple.stderr | 232 +++++++++++++++++-- 4 files changed, 343 insertions(+), 20 deletions(-) create mode 100644 tests/ui/offset-of/offset-of-tuple-nested.rs diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9e5f85dc7dc0a..df06115ef3b97 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1852,10 +1852,53 @@ impl<'a> Parser<'a> { let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( &TokenKind::CloseDelim(Delimiter::Parenthesis), seq_sep, - Parser::parse_field_name, + |this| { + let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = this.token.kind + else { + return Ok(thin_vec![this.parse_field_name()?]); + }; + let res = match this.break_up_float(symbol) { + // 1e2 + DestructuredFloat::Single(sym, sp) => { + this.bump(); + thin_vec![Ident::new(sym, sp)] + } + // 1. + DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { + assert!(suffix.is_none()); + // Analogous to Self::break_and_eat + this.token_cursor.break_last_token = true; + // This might work, in cases like `1. 2.3`, and might not, + // in cases like `offset_of!(Ty, 1.)`. + this.token = Token::new(token::Ident(sym, false), sym_span); + this.bump_with((Token::new(token::Dot, dot_span), this.token_spacing)); + thin_vec![Ident::new(sym, sym_span)] + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + symbol1, + ident1_span, + _dot_span, + symbol2, + ident2_span, + ) => { + this.bump(); + thin_vec![ + Ident::new(symbol1, ident1_span), + Ident::new(symbol2, ident2_span) + ] + } + DestructuredFloat::Error => { + this.bump(); + thin_vec![Ident::new(symbol, this.prev_token.span)] + } + }; + Ok(res) + }, )?; + let fields = fields.into_iter().flatten().collect::>(); let span = lo.to(self.token.span); - Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into()))) + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into()))) } /// Returns a string literal if the next token is a string literal. diff --git a/tests/ui/offset-of/offset-of-tuple-nested.rs b/tests/ui/offset-of/offset-of-tuple-nested.rs new file mode 100644 index 0000000000000..00fbb6bf8f407 --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple-nested.rs @@ -0,0 +1,32 @@ +// run-pass +// Test for issue #112204 -- make sure this goes through the entire compilation pipeline, +// similar to why `offset-of-unsized.rs` is also build-pass + +#![feature(offset_of)] +#![feature(builtin_syntax)] + +use std::mem::offset_of; + +type ComplexTup = ((u8, (u8, (u8, u16), u8)), (u8, u32, u16)); + +fn main() { + println!("{}", offset_of!(((u8, u8), u8), 0)); + println!("{}", offset_of!(((u8, u8), u8), 1)); + println!("{}", offset_of!(((u8, (u8, u8)), (u8, u8, u8)), 0.1.0)); + + // Complex case: do all combinations of spacings because the spacing determines what gets + // sent to the lexer. + println!("{}", offset_of!(ComplexTup, 0.1.1.1)); + println!("{}", builtin # offset_of(ComplexTup, 0. 1.1.1)); + println!("{}", offset_of!(ComplexTup, 0 . 1.1.1)); + println!("{}", offset_of!(ComplexTup, 0 .1.1.1)); + println!("{}", offset_of!(ComplexTup, 0.1 .1.1)); + println!("{}", offset_of!(ComplexTup, 0.1 . 1.1)); + println!("{}", offset_of!(ComplexTup, 0.1. 1.1)); + println!("{}", builtin # offset_of(ComplexTup, 0.1.1. 1)); + println!("{}", offset_of!(ComplexTup, 0.1.1 . 1)); + println!("{}", offset_of!(ComplexTup, 0.1.1 .1)); + + println!("{}", offset_of!(((u8, u16), (u32, u16, u8)), 0.0)); + println!("{}", offset_of!(((u8, u16), (u32, u16, u8)), 1.2)); +} diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index 4077538b77f27..e31b037ee3e01 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -1,10 +1,54 @@ #![feature(offset_of)] #![feature(builtin_syntax)] +use std::mem::offset_of; + fn main() { - core::mem::offset_of!((u8, u8), _0); //~ ERROR no field `_0` - core::mem::offset_of!((u8, u8), +1); //~ ERROR no rules expected - core::mem::offset_of!((u8, u8), -1); //~ ERROR no rules expected + offset_of!((u8, u8), _0); //~ ERROR no field `_0` + offset_of!((u8, u8), 01); //~ ERROR no field `01` + offset_of!((u8, u8), 1e2); //~ ERROR no field `1e2` + offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index + offset_of!((u8, u8), +1); //~ ERROR no rules expected + offset_of!((u8, u8), -1); //~ ERROR no rules expected + offset_of!((u8, u8), 1.); //~ ERROR expected identifier, found `)` + offset_of!((u8, u8), 1 .); //~ ERROR unexpected end of macro + builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` - builtin # offset_of((u8, u8), +1); //~ ERROR expected identifier + builtin # offset_of((u8, u8), 01); //~ ERROR no field `01` + builtin # offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index + // We need to put these into curly braces, otherwise only one of the + // errors will be emitted and the others suppressed. + { builtin # offset_of((u8, u8), +1) }; //~ ERROR expected identifier, found `+` + { builtin # offset_of((u8, u8), 1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of((u8, u8), 1 .) }; //~ ERROR expected identifier, found `)` +} + +type ComplexTup = ((u8, (u8, u8)), u8); + +fn nested() { + offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2` + offset_of!(((u8, u16), (u32, u16, u8)), 1.2); + offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); //~ ERROR no field `0` + + // All combinations of spaces (this sends different tokens to the parser) + offset_of!(ComplexTup, 0.0.1.); //~ ERROR expected identifier + offset_of!(ComplexTup, 0 .0.1.); //~ ERROR unexpected end of macro + offset_of!(ComplexTup, 0 . 0.1.); //~ ERROR unexpected end of macro + offset_of!(ComplexTup, 0. 0.1.); //~ ERROR no rules expected + offset_of!(ComplexTup, 0.0 .1.); //~ ERROR expected identifier, found `)` + offset_of!(ComplexTup, 0.0 . 1.); //~ ERROR expected identifier, found `)` + offset_of!(ComplexTup, 0.0. 1.); //~ ERROR expected identifier, found `)` + + // Test for builtin too to ensure that the builtin syntax can also handle these cases + // We need to put these into curly braces, otherwise only one of the + // errors will be emitted and the others suppressed. + { builtin # offset_of(ComplexTup, 0.0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR expected identifier, found `)` } diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr index cc9ce0f34550c..954515f80a65e 100644 --- a/tests/ui/offset-of/offset-of-tuple.stderr +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -1,37 +1,241 @@ +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-tuple.rs:19:35 + | +LL | builtin # offset_of((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` + error: expected identifier, found `+` - --> $DIR/offset-of-tuple.rs:9:35 + --> $DIR/offset-of-tuple.rs:23:37 + | +LL | { builtin # offset_of((u8, u8), +1) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:24:39 + | +LL | { builtin # offset_of((u8, u8), 1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:25:40 + | +LL | { builtin # offset_of((u8, u8), 1 .) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:47:45 + | +LL | { builtin # offset_of(ComplexTup, 0.0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:48:46 + | +LL | { builtin # offset_of(ComplexTup, 0 .0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:49:47 + | +LL | { builtin # offset_of(ComplexTup, 0 . 0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:50:46 + | +LL | { builtin # offset_of(ComplexTup, 0. 0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:51:46 + | +LL | { builtin # offset_of(ComplexTup, 0.0 .1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:52:47 + | +LL | { builtin # offset_of(ComplexTup, 0.0 . 1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:53:46 + | +LL | { builtin # offset_of(ComplexTup, 0.0. 1.) }; + | ^ expected identifier + +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-tuple.rs:10:26 | -LL | builtin # offset_of((u8, u8), +1); - | ^ expected identifier +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` error: no rules expected the token `1` - --> $DIR/offset-of-tuple.rs:6:38 + --> $DIR/offset-of-tuple.rs:12:27 | -LL | core::mem::offset_of!((u8, u8), +1); - | ^ no rules expected this token in macro call +LL | offset_of!((u8, u8), +1); + | ^ no rules expected this token in macro call | = note: while trying to match sequence start error: no rules expected the token `1` - --> $DIR/offset-of-tuple.rs:7:38 + --> $DIR/offset-of-tuple.rs:13:27 | -LL | core::mem::offset_of!((u8, u8), -1); - | ^ no rules expected this token in macro call +LL | offset_of!((u8, u8), -1); + | ^ no rules expected this token in macro call | = note: while trying to match sequence start +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:14:5 + | +LL | offset_of!((u8, u8), 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:15:29 + | +LL | offset_of!((u8, u8), 1 .); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:36:5 + | +LL | offset_of!(ComplexTup, 0.0.1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:37:35 + | +LL | offset_of!(ComplexTup, 0 .0.1.); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:38:36 + | +LL | offset_of!(ComplexTup, 0 . 0.1.); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: no rules expected the token `0.1` + --> $DIR/offset-of-tuple.rs:39:31 + | +LL | offset_of!(ComplexTup, 0. 0.1.); + | ^^^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:40:5 + | +LL | offset_of!(ComplexTup, 0.0 .1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:41:5 + | +LL | offset_of!(ComplexTup, 0.0 . 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:42:5 + | +LL | offset_of!(ComplexTup, 0.0. 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple.rs:5:37 + --> $DIR/offset-of-tuple.rs:7:26 | -LL | core::mem::offset_of!((u8, u8), _0); - | ^^ +LL | offset_of!((u8, u8), _0); + | ^^ + +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:8:26 + | +LL | offset_of!((u8, u8), 01); + | ^^ + +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:9:26 + | +LL | offset_of!((u8, u8), 1e2); + | ^^^ + +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:10:26 + | +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ + +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:16:35 + | +LL | builtin # offset_of((u8, u8), 1e2); + | ^^^ error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple.rs:8:35 + --> $DIR/offset-of-tuple.rs:17:35 | LL | builtin # offset_of((u8, u8), _0); | ^^ -error: aborting due to 5 previous errors +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:18:35 + | +LL | builtin # offset_of((u8, u8), 01); + | ^^ + +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:19:35 + | +LL | builtin # offset_of((u8, u8), 1_u8); + | ^^^^ + +error[E0609]: no field `2` on type `(u8, u16)` + --> $DIR/offset-of-tuple.rs:31:47 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); + | ^ + +error[E0609]: no field `0` on type `u8` + --> $DIR/offset-of-tuple.rs:33:49 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); + | ^ + +error: aborting due to 33 previous errors For more information about this error, try `rustc --explain E0609`. From 9fb266b525938f71089f23b49d756d77f550521b Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 9 Jun 2023 00:07:39 +0200 Subject: [PATCH 786/806] Move parse_seq_to_before_end closure to own function --- compiler/rustc_parse/src/parser/expr.rs | 80 ++++++++++++------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index df06115ef3b97..cea2a71c98821 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1146,6 +1146,42 @@ impl<'a> Parser<'a> { } } + fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec> { + let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind + else { + return Ok(thin_vec![self.parse_field_name()?]); + }; + Ok(match self.break_up_float(symbol) { + // 1e2 + DestructuredFloat::Single(sym, sp) => { + self.bump(); + thin_vec![Ident::new(sym, sp)] + } + // 1. + DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { + assert!(suffix.is_none()); + // Analogous to `Self::break_and_eat` + self.token_cursor.break_last_token = true; + // This might work, in cases like `1. 2`, and might not, + // in cases like `offset_of!(Ty, 1.)`. It depends on what comes + // after the float-like token, and therefore we have to make + // the other parts of the parser think that there is a dot literal. + self.token = Token::new(token::Ident(sym, false), sym_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + thin_vec![Ident::new(sym, sym_span)] + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => { + self.bump(); + thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)] + } + DestructuredFloat::Error => { + self.bump(); + thin_vec![Ident::new(symbol, self.prev_token.span)] + } + }) + } + fn parse_expr_tuple_field_access( &mut self, lo: Span, @@ -1852,49 +1888,7 @@ impl<'a> Parser<'a> { let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( &TokenKind::CloseDelim(Delimiter::Parenthesis), seq_sep, - |this| { - let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = this.token.kind - else { - return Ok(thin_vec![this.parse_field_name()?]); - }; - let res = match this.break_up_float(symbol) { - // 1e2 - DestructuredFloat::Single(sym, sp) => { - this.bump(); - thin_vec![Ident::new(sym, sp)] - } - // 1. - DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { - assert!(suffix.is_none()); - // Analogous to Self::break_and_eat - this.token_cursor.break_last_token = true; - // This might work, in cases like `1. 2.3`, and might not, - // in cases like `offset_of!(Ty, 1.)`. - this.token = Token::new(token::Ident(sym, false), sym_span); - this.bump_with((Token::new(token::Dot, dot_span), this.token_spacing)); - thin_vec![Ident::new(sym, sym_span)] - } - // 1.2 | 1.2e3 - DestructuredFloat::MiddleDot( - symbol1, - ident1_span, - _dot_span, - symbol2, - ident2_span, - ) => { - this.bump(); - thin_vec![ - Ident::new(symbol1, ident1_span), - Ident::new(symbol2, ident2_span) - ] - } - DestructuredFloat::Error => { - this.bump(); - thin_vec![Ident::new(symbol, this.prev_token.span)] - } - }; - Ok(res) - }, + Parser::parse_field_name_maybe_tuple, )?; let fields = fields.into_iter().flatten().collect::>(); let span = lo.to(self.token.span); From d5e25d40c969009d0f40cbf6280339f2839e19c4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 8 Jun 2023 23:12:53 +0000 Subject: [PATCH 787/806] deduplicate identical region constraints --- .../src/solve/eval_ctxt/canonical.rs | 6 +++- tests/ui/traits/new-solver/dedup-regions.rs | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/ui/traits/new-solver/dedup-regions.rs diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index bca2343e42422..72b3c3d01804a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -11,6 +11,7 @@ use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; @@ -147,7 +148,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Cannot use `take_registered_region_obligations` as we may compute the response // inside of a `probe` whenever we have multiple choices inside of the solver. let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); - let region_constraints = self.infcx.with_region_constraints(|region_constraints| { + let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { make_query_region_constraints( self.tcx(), region_obligations @@ -157,6 +158,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) }); + let mut seen = FxHashSet::default(); + region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); + let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); // Only return opaque type keys for newly-defined opaques opaque_types.retain(|(a, _)| { diff --git a/tests/ui/traits/new-solver/dedup-regions.rs b/tests/ui/traits/new-solver/dedup-regions.rs new file mode 100644 index 0000000000000..f376f39a5a662 --- /dev/null +++ b/tests/ui/traits/new-solver/dedup-regions.rs @@ -0,0 +1,31 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +struct A(*mut ()); + +unsafe impl Send for A where A: 'static {} + +macro_rules! mk { + ($name:ident $ty:ty) => { + struct $name($ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty); + }; +} + +mk!(B A); +mk!(C B); +mk!(D C); +mk!(E D); +mk!(F E); +mk!(G F); +mk!(H G); +mk!(I H); +mk!(J I); +mk!(K J); +mk!(L K); +mk!(M L); + +fn needs_send() {} + +fn main() { + needs_send::(); +} From 80e9ca93981f6fa8fae4e95111b78b9093348b2c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 9 Jun 2023 00:20:33 +0000 Subject: [PATCH 788/806] Don't print Interned or PrivateZst --- compiler/rustc_data_structures/src/intern.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs index ba94f3776eb90..e0f8c350c2a86 100644 --- a/compiler/rustc_data_structures/src/intern.rs +++ b/compiler/rustc_data_structures/src/intern.rs @@ -1,5 +1,6 @@ use crate::stable_hasher::{HashStable, StableHasher}; use std::cmp::Ordering; +use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::ptr; @@ -20,7 +21,6 @@ mod private { /// The `PrivateZst` field means you can pattern match with `Interned(v, _)` /// but you can only construct a `Interned` with `new_unchecked`, and not /// directly. -#[derive(Debug)] #[rustc_pass_by_value] pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst); @@ -108,5 +108,11 @@ where } } +impl Debug for Interned<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + #[cfg(test)] mod tests; From 4b1d13d9841c815915433ca2a3088a8e3e97ad96 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 8 Jun 2023 14:30:01 +0200 Subject: [PATCH 789/806] List matching impls on type aliases --- src/librustdoc/html/render/mod.rs | 90 ++++++++++++++++++++---- src/librustdoc/html/render/print_item.rs | 48 ++++++++----- 2 files changed, 110 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f205ff15ec3d0..baffee0964da9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -53,12 +53,15 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::middle::stability; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_span::{ symbol::{sym, Symbol}, BytePos, FileName, RealFileName, }; +use rustc_trait_selection::traits::ObligationCtxt; use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; @@ -1112,15 +1115,47 @@ fn render_assoc_items<'a, 'cx: 'a>( containing_item: &'a clean::Item, it: DefId, what: AssocItemRender<'a>, + aliased_type: Option, ) -> impl fmt::Display + 'a + Captures<'cx> { let mut derefs = DefIdSet::default(); derefs.insert(it); display_fn(move |f| { - render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs); + render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type); Ok(()) }) } +/// Check whether `impl_def_id` may apply to *some instantiation* of `item_def_id`. +fn is_valid_impl_for(tcx: TyCtxt<'_>, item_def_id: DefId, impl_def_id: DefId) -> bool { + let infcx = tcx.infer_ctxt().intercrate(true).build(); + let ocx = ObligationCtxt::new(&infcx); + let param_env = ParamEnv::empty(); + + let alias_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, item_def_id); + let alias_ty = tcx.type_of(item_def_id).subst(tcx, alias_substs); + let alias_bounds = tcx.predicates_of(item_def_id).instantiate(tcx, alias_substs); + + let impl_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, impl_def_id); + let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_bounds = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs); + + if ocx.eq(&ObligationCause::dummy(), param_env, impl_self_ty, alias_ty).is_err() { + return false; + } + ocx.register_obligations( + alias_bounds + .iter() + .chain(impl_bounds) + .map(|(p, _)| Obligation::new(tcx, ObligationCause::dummy(), param_env, p)), + ); + + let errors = ocx.select_where_possible(); + errors.is_empty() +} + +// If `aliased_type` is `Some`, it means `it` is a type alias and `aliased_type` is the "actual" +// type aliased behind `it`. It is used to check whether or not the implementation of the aliased +// type can be displayed on the alias doc page. fn render_assoc_items_inner( mut w: &mut dyn fmt::Write, cx: &mut Context<'_>, @@ -1128,12 +1163,28 @@ fn render_assoc_items_inner( it: DefId, what: AssocItemRender<'_>, derefs: &mut DefIdSet, + aliased_type: Option, ) { info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); let cache = &shared.cache; - let Some(v) = cache.impls.get(&it) else { return }; - let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); + let empty = Vec::new(); + let v = match cache.impls.get(&it) { + Some(v) => v, + None => &empty, + }; + let v2 = match aliased_type { + Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty), + None => &empty, + }; + if v.is_empty() && v2.is_empty() { + return; + } + let mut saw_impls = FxHashSet::default(); + let (non_trait, traits): (Vec<_>, _) = + v.iter().chain(v2).partition(|i| i.inner_impl().trait_.is_none()); + let tcx = cx.tcx(); + let is_alias = aliased_type.is_some(); if !non_trait.is_empty() { let mut tmp_buf = Buffer::html(); let (render_mode, id, class_html) = match what { @@ -1165,6 +1216,12 @@ fn render_assoc_items_inner( }; let mut impls_buf = Buffer::html(); for i in &non_trait { + if !saw_impls.insert(i.def_id()) { + continue; + } + if is_alias && !is_valid_impl_for(tcx, it, i.def_id()) { + continue; + } render_impl( &mut impls_buf, cx, @@ -1193,9 +1250,14 @@ fn render_assoc_items_inner( if !traits.is_empty() { let deref_impl = traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); - if let Some(impl_) = deref_impl { + if let Some(impl_) = deref_impl && + (!is_alias || is_valid_impl_for(tcx, it, impl_.def_id())) + { let has_deref_mut = - traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + traits.iter().any(|t| { + t.trait_did() == cx.tcx().lang_items().deref_mut_trait() && + (!is_alias || is_valid_impl_for(tcx, it, t.def_id())) + }); render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs); } @@ -1205,10 +1267,14 @@ fn render_assoc_items_inner( return; } - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits + .into_iter() + .filter(|t| saw_impls.insert(t.def_id())) + .partition(|t| t.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete + .into_iter() + .filter(|t| !is_alias || is_valid_impl_for(tcx, it, t.def_id())) + .partition(|t| t.inner_impl().kind.is_blanket()); render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } @@ -1247,10 +1313,10 @@ fn render_deref_methods( return; } } - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None); } else if let Some(prim) = target.primitive_type() { if let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None); } } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 4be0a7b4a5fb8..01089ed348bfd 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -260,7 +260,7 @@ fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>( display_fn(move |f| { let (item, mut cx) = templ.item_and_mut_cx(); let def_id = item.item_id.expect_def_id(); - let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All); + let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None); write!(f, "{v}") }) } @@ -893,7 +893,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } // If there are methods directly on this trait object, render them here. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ); let cloned_shared = Rc::clone(&cx.shared); let cache = &cloned_shared.cache; @@ -1125,8 +1129,12 @@ fn item_trait_alias( // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) - .unwrap(); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } fn item_opaque_ty( @@ -1154,8 +1162,12 @@ fn item_opaque_ty( // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) - .unwrap(); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { @@ -1179,11 +1191,11 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let def_id = it.item_id.expect_def_id(); - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!( + w, + "{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache())) + ); write!(w, "{}", document_type_layout(cx, def_id)); } @@ -1423,7 +1435,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: write!(w, ""); } let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)); write!(w, "{}", document_type_layout(cx, def_id)); } @@ -1466,7 +1478,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite let def_id = it.item_id.expect_def_id(); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap(); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).unwrap(); } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. @@ -1571,7 +1583,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } } let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)); write!(w, "{}", document_type_layout(cx, def_id)); } @@ -1606,8 +1618,12 @@ fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean:: }); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) - .unwrap(); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { From 6f552c800b38b3e71c5e33a295e8b490d2018c71 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 8 Jun 2023 19:51:14 +0200 Subject: [PATCH 790/806] Add regression test for #32077 --- tests/rustdoc/issue-32077-type-alias-impls.rs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/rustdoc/issue-32077-type-alias-impls.rs diff --git a/tests/rustdoc/issue-32077-type-alias-impls.rs b/tests/rustdoc/issue-32077-type-alias-impls.rs new file mode 100644 index 0000000000000..555d0579bee79 --- /dev/null +++ b/tests/rustdoc/issue-32077-type-alias-impls.rs @@ -0,0 +1,59 @@ +// Regression test for . + +#![crate_name = "foo"] + +pub struct GenericStruct(T); + +impl GenericStruct { + pub fn on_gen(arg: T) {} +} + +impl GenericStruct { + pub fn on_u32(arg: u32) {} +} + +pub trait Foo {} +pub trait Bar {} + +impl Foo for GenericStruct {} +impl Bar for GenericStruct {} + +// @has 'foo/type.TypedefStruct.html' +// We check that we have the implementation of the type alias itself. +// @has - '//*[@id="impl-TypedefStruct"]/h3' 'impl TypedefStruct' +// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()' +// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl GenericStruct' +// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)' +// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl Foo for GenericStruct' +// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs. +// @!has - '//h3' 'impl Bar for GenericStruct {}' +// Same goes for the `Deref` impl. +// @!has - '//h2' 'Methods from Deref' +pub type TypedefStruct = GenericStruct; + +impl TypedefStruct { + pub fn on_alias() {} +} + +impl std::ops::Deref for GenericStruct { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct Wrap(GenericStruct); + +// @has 'foo/type.Alias.html' +// @has - '//h2' 'Methods from Deref' +// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl Deref for Wrap' +pub type Alias = Wrap; + +impl std::ops::Deref for Wrap { + type Target = GenericStruct; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} From 2ce7cd906bde70d8cbd9b07b31c6a7bf1131c345 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 8 Jun 2023 22:48:29 +0200 Subject: [PATCH 791/806] Fix intra-doc links from pointer appearing in windows HANDLE type alias --- library/core/src/ptr/mut_ptr.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index c6f43857887c9..e753647ff7868 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -106,7 +106,7 @@ impl *mut T { /// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit /// coercion. /// - /// [`cast_mut`]: #method.cast_mut + /// [`cast_mut`]: pointer::cast_mut #[stable(feature = "ptr_const_cast", since = "1.65.0")] #[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")] #[inline(always)] @@ -117,7 +117,7 @@ impl *mut T { /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. - /// The inverse method is [`from_bits`](#method.from_bits-1). + /// The inverse method is [`from_bits`](pointer#method.from_bits-1). /// /// In particular, `*p as usize` and `p as usize` will both compile for /// pointers to numeric types but do very different things, so using this @@ -153,7 +153,7 @@ impl *mut T { /// Creates a pointer from its raw bits. /// /// This is equivalent to `as *mut T`, but is more specific to enhance readability. - /// The inverse method is [`to_bits`](#method.to_bits-1). + /// The inverse method is [`to_bits`](pointer#method.to_bits-1). /// /// # Examples /// @@ -303,7 +303,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_mut`]. /// - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// [`as_mut`]: #method.as_mut /// /// # Safety @@ -369,7 +369,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_uninit_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_mut`]: #method.as_uninit_mut /// /// # Safety @@ -624,7 +624,7 @@ impl *mut T { /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety /// @@ -689,7 +689,7 @@ impl *mut T { /// For the shared counterpart see [`as_uninit_ref`]. /// /// [`as_mut`]: #method.as_mut - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// /// # Safety /// @@ -779,7 +779,7 @@ impl *mut T { /// /// This function is the inverse of [`offset`]. /// - /// [`offset`]: #method.offset-1 + /// [`offset`]: pointer#method.offset-1 /// /// # Safety /// @@ -2051,7 +2051,7 @@ impl *mut [T] { /// /// For the mutable counterpart see [`as_uninit_slice_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut /// /// # Safety From 30f84c4d1755de683e565a03f269878796f71bdf Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Fri, 9 Jun 2023 16:11:23 +0800 Subject: [PATCH 792/806] Improve document of `unsafe_code` lint Signed-off-by: Eval EXEC --- compiler/rustc_lint/src/builtin.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8c0956a618dea..ff2989112af1a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -286,7 +286,9 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { } declare_lint! { - /// The `unsafe_code` lint catches usage of `unsafe` code. + /// The `unsafe_code` lint catches usage of `unsafe` code and other + /// potentially unsound constructs like `no_mangle`, `export_name`, + /// and `link_section`. /// /// ### Example /// @@ -297,17 +299,29 @@ declare_lint! { /// /// } /// } + /// + /// #[no_mangle] + /// fn func_0() { } + /// + /// #[export_name = "exported_symbol_name"] + /// pub fn name_in_rust() { } + /// + /// #[no_mangle] + /// #[link_section = ".example_section"] + /// pub static VAR1: u32 = 1; /// ``` /// /// {{produces}} /// /// ### Explanation /// - /// This lint is intended to restrict the usage of `unsafe`, which can be - /// difficult to use correctly. + /// This lint is intended to restrict the usage of `unsafe` blocks and other + /// constructs (including, but not limited to `no_mangle`, `link_section` + /// and `export_name` attributes) wrong usage of which causes undefined + /// behavior. UNSAFE_CODE, Allow, - "usage of `unsafe` code" + "usage of `unsafe` code and other potentially unsound constructs" } declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); From 7f79ceb438321810a8199fb099067cd648c638da Mon Sep 17 00:00:00 2001 From: Matthew Esposito Date: Fri, 9 Jun 2023 10:47:41 -0400 Subject: [PATCH 793/806] Compile rustc_driver by default --- src/bootstrap/doc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index d6210ed59c4d1..fef7e4db9ab85 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -667,7 +667,7 @@ impl Step for Rustc { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(mut self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; @@ -725,6 +725,11 @@ impl Step for Rustc { cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); let mut to_open = None; + + if self.crates.is_empty() { + self.crates = INTERNER.intern_list(vec!["rustc_driver".to_owned()]); + }; + for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. From 6b0c7c4f01d3e6c90d10847471c7a2256346dd3e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 Jun 2023 16:49:19 +0200 Subject: [PATCH 794/806] Change format of rustdoc-js tests by putting `query` and `correction` directly alongside the expected values --- src/tools/rustdoc-js/tester.js | 90 ++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 270704ebffde6..416517d15f5db 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -22,6 +22,10 @@ function contentToDiffLine(key, value) { return `"${key}": "${value}",`; } +function shouldIgnoreField(fieldName) { + return fieldName === "query" || fieldName === "correction"; +} + // This function is only called when no matching result was found and therefore will only display // the diff between the two items. function betterLookingDiff(entry, data) { @@ -135,6 +139,9 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { } else if (expected !== null && typeof expected !== "undefined" && expected.constructor == Object) { // eslint-disable-line eqeqeq for (const key in expected) { + if (shouldIgnoreField(key)) { + continue; + } if (!Object.prototype.hasOwnProperty.call(expected, key)) { continue; } @@ -184,6 +191,9 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) { const error_text = []; for (const key in expected) { + if (shouldIgnoreField(key)) { + continue; + } if (!Object.prototype.hasOwnProperty.call(expected, key)) { continue; } @@ -260,41 +270,49 @@ function checkResult(error_text, loadedFile, displaySuccess) { return 1; } -function runCheck(loadedFile, key, callback) { - const expected = loadedFile[key]; - const query = loadedFile.QUERY; - - if (Array.isArray(query)) { - if (!Array.isArray(expected)) { - console.log("FAILED"); - console.log(`==> If QUERY variable is an array, ${key} should be an array too`); - return 1; - } else if (query.length !== expected.length) { - console.log("FAILED"); - console.log(`==> QUERY variable should have the same length as ${key}`); - return 1; +function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { + if (typeof entry.query !== "string") { + console.log("FAILED"); + console.log("==> Missing `query` field"); + return false; + } + let error_text = callback(entry.query, entry, extra ? "[ query `" + entry.query + "`]" : ""); + if (checkResult(error_text, loadedFile, false) !== 0) { + return false; + } + if (entry.correction !== undefined) { + error_text = runCorrections(entry.query, entry.correction, getCorrections, loadedFile); + if (checkResult(error_text, loadedFile, false) !== 0) { + return false; } - for (let i = 0; i < query.length; ++i) { - const error_text = callback(query[i], expected[i], "[ query `" + query[i] + "`]"); - if (checkResult(error_text, loadedFile, false) !== 0) { + } + return true; +} + +function runCheck(loadedFile, key, getCorrections, callback) { + const expected = loadedFile[key]; + + if (Array.isArray(expected)) { + for (const entry of expected) { + if (!runCheckInner(callback, loadedFile, entry, getCorrections, true)) { return 1; } } - console.log("OK"); - } else { - const error_text = callback(query, expected, ""); - if (checkResult(error_text, loadedFile, true) !== 0) { - return 1; - } + } else if (!runCheckInner(callback, loadedFile, expected, getCorrections, false)) { + return 1; } + console.log("OK"); return 0; } +function hasCheck(content, checkName) { + return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); +} + function runChecks(testFile, doSearch, parseQuery, getCorrections) { let checkExpected = false; let checkParsed = false; - let checkCorrections = false; - let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;"; + let testFileContent = readFile(testFile); if (testFileContent.indexOf("FILTER_CRATE") !== -1) { testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;"; @@ -302,21 +320,17 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) { testFileContent += "exports.FILTER_CRATE = null;"; } - if (testFileContent.indexOf("\nconst EXPECTED") !== -1) { + if (hasCheck(testFileContent, "EXPECTED")) { testFileContent += "exports.EXPECTED = EXPECTED;"; checkExpected = true; } - if (testFileContent.indexOf("\nconst PARSED") !== -1) { + if (hasCheck(testFileContent, "PARSED")) { testFileContent += "exports.PARSED = PARSED;"; checkParsed = true; } - if (testFileContent.indexOf("\nconst CORRECTIONS") !== -1) { - testFileContent += "exports.CORRECTIONS = CORRECTIONS;"; - checkCorrections = true; - } - if (!checkParsed && !checkExpected && !checkCorrections) { + if (!checkParsed && !checkExpected) { console.log("FAILED"); - console.log("==> At least `PARSED`, `EXPECTED`, or `CORRECTIONS` is needed!"); + console.log("==> At least `PARSED` or `EXPECTED` is needed!"); return 1; } @@ -324,20 +338,15 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) { let res = 0; if (checkExpected) { - res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => { + res += runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { return runSearch(query, expected, doSearch, loadedFile, text); }); } if (checkParsed) { - res += runCheck(loadedFile, "PARSED", (query, expected, text) => { + res += runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { return runParser(query, expected, parseQuery, text); }); } - if (checkCorrections) { - res += runCheck(loadedFile, "CORRECTIONS", (query, expected) => { - return runCorrections(query, expected, getCorrections, loadedFile); - }); - } return res; } @@ -367,8 +376,7 @@ function loadSearchJS(doc_folder, resource_suffix) { }, getCorrections: function(queryStr, filterCrate, currentCrate) { const parsedQuery = searchModule.parseQuery(queryStr); - searchModule.execQuery(parsedQuery, searchWords, - filterCrate, currentCrate); + searchModule.execQuery(parsedQuery, searchWords, filterCrate, currentCrate); return parsedQuery.correction; }, parseQuery: searchModule.parseQuery, From 9803651ee84545f8913b6fe9e76c8bd13603bb6e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 Jun 2023 16:49:38 +0200 Subject: [PATCH 795/806] Update rustdoc-js* format --- tests/rustdoc-js-std/alias-1.js | 3 +- tests/rustdoc-js-std/alias-2.js | 3 +- tests/rustdoc-js-std/alias-3.js | 3 +- tests/rustdoc-js-std/alias-4.js | 3 +- tests/rustdoc-js-std/alias.js | 3 +- tests/rustdoc-js-std/asrawfd.js | 3 +- tests/rustdoc-js-std/basic.js | 3 +- tests/rustdoc-js-std/deduplication.js | 3 +- tests/rustdoc-js-std/enum-option.js | 3 +- tests/rustdoc-js-std/filter-crate.js | 2 +- tests/rustdoc-js-std/fn-forget.js | 3 +- tests/rustdoc-js-std/from_u.js | 3 +- tests/rustdoc-js-std/keyword.js | 3 +- tests/rustdoc-js-std/macro-check.js | 3 +- tests/rustdoc-js-std/macro-print.js | 3 +- tests/rustdoc-js-std/never.js | 3 +- .../rustdoc-js-std/option-type-signatures.js | 7 +- tests/rustdoc-js-std/parser-errors.js | 87 +++++++++---------- tests/rustdoc-js-std/parser-filter.js | 21 ++--- tests/rustdoc-js-std/parser-generics.js | 15 ++-- tests/rustdoc-js-std/parser-ident.js | 15 ++-- tests/rustdoc-js-std/parser-literal.js | 3 +- tests/rustdoc-js-std/parser-paths.js | 6 +- tests/rustdoc-js-std/parser-quote.js | 17 ++-- tests/rustdoc-js-std/parser-returned.js | 13 ++- tests/rustdoc-js-std/parser-separators.js | 17 ++-- tests/rustdoc-js-std/parser-weird-queries.js | 16 ++-- tests/rustdoc-js-std/path-ordering.js | 5 +- tests/rustdoc-js-std/primitive.js | 17 ++-- tests/rustdoc-js-std/println-typo.js | 2 +- tests/rustdoc-js-std/quoted.js | 2 +- tests/rustdoc-js-std/reference-shrink.js | 3 +- tests/rustdoc-js-std/regex.js | 3 +- .../rustdoc-js-std/return-specific-literal.js | 3 +- tests/rustdoc-js-std/return-specific.js | 3 +- tests/rustdoc-js-std/should-fail.js | 3 +- tests/rustdoc-js-std/string-from_ut.js | 3 +- tests/rustdoc-js-std/struct-vec.js | 3 +- tests/rustdoc-js-std/typed-query.js | 2 +- tests/rustdoc-js-std/vec-new.js | 3 +- tests/rustdoc-js/basic.js | 3 +- tests/rustdoc-js/doc-alias-filter-out.js | 3 +- tests/rustdoc-js/doc-alias-filter.js | 3 +- tests/rustdoc-js/doc-alias-whitespace.js | 5 +- tests/rustdoc-js/doc-alias.js | 69 +++++---------- tests/rustdoc-js/exact-match.js | 3 +- tests/rustdoc-js/foreign-type-path.js | 3 +- tests/rustdoc-js/generics-impl.js | 30 ++----- tests/rustdoc-js/generics-multi-trait.js | 9 +- tests/rustdoc-js/generics-nested.js | 15 +--- tests/rustdoc-js/generics-trait.js | 26 ++---- tests/rustdoc-js/generics.js | 30 ++----- tests/rustdoc-js/impl-trait.js | 18 ++-- tests/rustdoc-js/macro-search.js | 3 +- tests/rustdoc-js/module-substring.js | 3 +- tests/rustdoc-js/path-ordering.js | 3 +- tests/rustdoc-js/primitive.js | 13 ++- tests/rustdoc-js/prototype.js | 4 +- tests/rustdoc-js/raw-pointer.js | 18 ++-- tests/rustdoc-js/reexport.js | 4 +- tests/rustdoc-js/search-bag-semantics.js | 7 +- tests/rustdoc-js/search-short-types.js | 3 +- tests/rustdoc-js/slice-array.js | 27 ++---- tests/rustdoc-js/struct-like-variant.js | 3 +- tests/rustdoc-js/substring.js | 3 +- tests/rustdoc-js/summaries.js | 5 +- tests/rustdoc-js/where-clause.js | 7 +- 67 files changed, 236 insertions(+), 400 deletions(-) diff --git a/tests/rustdoc-js-std/alias-1.js b/tests/rustdoc-js-std/alias-1.js index 7c6327fcdd7ca..b27b3da217966 100644 --- a/tests/rustdoc-js-std/alias-1.js +++ b/tests/rustdoc-js-std/alias-1.js @@ -1,6 +1,5 @@ -const QUERY = '&'; - const EXPECTED = { + 'query': '&', 'others': [ { 'path': 'std', 'name': 'reference' }, ], diff --git a/tests/rustdoc-js-std/alias-2.js b/tests/rustdoc-js-std/alias-2.js index 798fa29efbd2d..5735b573bcbda 100644 --- a/tests/rustdoc-js-std/alias-2.js +++ b/tests/rustdoc-js-std/alias-2.js @@ -1,6 +1,5 @@ -const QUERY = '+'; - const EXPECTED = { + 'query': '+', 'others': [ { 'path': 'std::ops', 'name': 'AddAssign' }, { 'path': 'std::ops', 'name': 'Add' }, diff --git a/tests/rustdoc-js-std/alias-3.js b/tests/rustdoc-js-std/alias-3.js index 392b1e8183786..ed3776b3c2ae0 100644 --- a/tests/rustdoc-js-std/alias-3.js +++ b/tests/rustdoc-js-std/alias-3.js @@ -1,6 +1,5 @@ -const QUERY = '!'; - const EXPECTED = { + 'query': '!', 'others': [ { 'path': 'std', 'name': 'never' }, ], diff --git a/tests/rustdoc-js-std/alias-4.js b/tests/rustdoc-js-std/alias-4.js index bf2bb4d2981fc..35840a472c10b 100644 --- a/tests/rustdoc-js-std/alias-4.js +++ b/tests/rustdoc-js-std/alias-4.js @@ -1,6 +1,5 @@ -const QUERY = '<'; - const EXPECTED = { + 'query': '<', 'others': [ { 'name': 'Ord' }, ], diff --git a/tests/rustdoc-js-std/alias.js b/tests/rustdoc-js-std/alias.js index 2b709c99119ae..bf707fa03dc2a 100644 --- a/tests/rustdoc-js-std/alias.js +++ b/tests/rustdoc-js-std/alias.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = '['; - const EXPECTED = { + 'query': '[', 'others': [ { 'path': 'std', 'name': 'slice' }, { 'path': 'std::ops', 'name': 'IndexMut' }, diff --git a/tests/rustdoc-js-std/asrawfd.js b/tests/rustdoc-js-std/asrawfd.js index 369a34f9c6eb7..5b3cfeabbcdd2 100644 --- a/tests/rustdoc-js-std/asrawfd.js +++ b/tests/rustdoc-js-std/asrawfd.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'RawFd::as_raw_fd'; - const EXPECTED = { + 'query': 'RawFd::as_raw_fd', 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. diff --git a/tests/rustdoc-js-std/basic.js b/tests/rustdoc-js-std/basic.js index 824cac7108332..baff24b0af699 100644 --- a/tests/rustdoc-js-std/basic.js +++ b/tests/rustdoc-js-std/basic.js @@ -1,6 +1,5 @@ -const QUERY = 'String'; - const EXPECTED = { + 'query': 'String', 'others': [ { 'path': 'std::string', 'name': 'String' }, { 'path': 'std::ffi', 'name': 'CString' }, diff --git a/tests/rustdoc-js-std/deduplication.js b/tests/rustdoc-js-std/deduplication.js index f02f6cf55ed21..51279dd5ed467 100644 --- a/tests/rustdoc-js-std/deduplication.js +++ b/tests/rustdoc-js-std/deduplication.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'is_nan'; - const EXPECTED = { + 'query': 'is_nan', 'others': [ { 'path': 'std::f32', 'name': 'is_nan' }, { 'path': 'std::f64', 'name': 'is_nan' }, diff --git a/tests/rustdoc-js-std/enum-option.js b/tests/rustdoc-js-std/enum-option.js index 902e09069108d..216dafe3b129e 100644 --- a/tests/rustdoc-js-std/enum-option.js +++ b/tests/rustdoc-js-std/enum-option.js @@ -1,6 +1,5 @@ -const QUERY = 'enum:Option'; - const EXPECTED = { + 'query': 'enum:Option', 'others': [ { 'path': 'std::option', 'name': 'Option' }, ], diff --git a/tests/rustdoc-js-std/filter-crate.js b/tests/rustdoc-js-std/filter-crate.js index b47a1fefa41d0..95f2969d29924 100644 --- a/tests/rustdoc-js-std/filter-crate.js +++ b/tests/rustdoc-js-std/filter-crate.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = '"hashmap"'; const FILTER_CRATE = 'core'; const EXPECTED = { + 'query': 'hashmap', 'others': [ ], }; diff --git a/tests/rustdoc-js-std/fn-forget.js b/tests/rustdoc-js-std/fn-forget.js index 66a5fcaa7813d..addecf4e44fe4 100644 --- a/tests/rustdoc-js-std/fn-forget.js +++ b/tests/rustdoc-js-std/fn-forget.js @@ -1,6 +1,5 @@ -const QUERY = 'fn:forget'; - const EXPECTED = { + 'query': 'fn:forget', 'others': [ { 'path': 'std::mem', 'name': 'forget' }, { 'path': 'std::fmt', 'name': 'format' }, diff --git a/tests/rustdoc-js-std/from_u.js b/tests/rustdoc-js-std/from_u.js index e3f3cd436aa6a..7c9375ba529a8 100644 --- a/tests/rustdoc-js-std/from_u.js +++ b/tests/rustdoc-js-std/from_u.js @@ -1,6 +1,5 @@ -const QUERY = 'from_u'; - const EXPECTED = { + 'query': 'from_u', 'others': [ { 'path': 'std::char', 'name': 'from_u32' }, { 'path': 'std::str', 'name': 'from_utf8' }, diff --git a/tests/rustdoc-js-std/keyword.js b/tests/rustdoc-js-std/keyword.js index 868ddd7b6dceb..b85ba34138bae 100644 --- a/tests/rustdoc-js-std/keyword.js +++ b/tests/rustdoc-js-std/keyword.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'fn'; - const EXPECTED = { + 'query': 'fn', 'others': [ { 'path': 'std', 'name': 'fn', ty: 15 }, // 15 is for primitive types { 'path': 'std', 'name': 'fn', ty: 21 }, // 21 is for keywords diff --git a/tests/rustdoc-js-std/macro-check.js b/tests/rustdoc-js-std/macro-check.js index 242e0cbf5f4de..c22b1753fd71b 100644 --- a/tests/rustdoc-js-std/macro-check.js +++ b/tests/rustdoc-js-std/macro-check.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'panic'; - const EXPECTED = { + 'query': 'panic', 'others': [ { 'path': 'std', 'name': 'panic', ty: 14 }, // 15 is for macros { 'path': 'std', 'name': 'panic', ty: 0 }, // 0 is for modules diff --git a/tests/rustdoc-js-std/macro-print.js b/tests/rustdoc-js-std/macro-print.js index 1b4c7b4057020..2ef1c89e49b0f 100644 --- a/tests/rustdoc-js-std/macro-print.js +++ b/tests/rustdoc-js-std/macro-print.js @@ -1,6 +1,5 @@ -const QUERY = 'macro:print'; - const EXPECTED = { + 'query': 'macro:print', 'others': [ { 'path': 'std', 'name': 'print' }, { 'path': 'std', 'name': 'println' }, diff --git a/tests/rustdoc-js-std/never.js b/tests/rustdoc-js-std/never.js index 392b1e8183786..ed3776b3c2ae0 100644 --- a/tests/rustdoc-js-std/never.js +++ b/tests/rustdoc-js-std/never.js @@ -1,6 +1,5 @@ -const QUERY = '!'; - const EXPECTED = { + 'query': '!', 'others': [ { 'path': 'std', 'name': 'never' }, ], diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js index 6bf421a213560..8f6b0450dd319 100644 --- a/tests/rustdoc-js-std/option-type-signatures.js +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -1,15 +1,12 @@ -const QUERY = [ - 'option, fnonce -> option', - 'option -> default', -]; - const EXPECTED = [ { + 'query': 'option, fnonce -> option', 'others': [ { 'path': 'std::option::Option', 'name': 'map' }, ], }, { + 'query': 'option -> default', 'others': [ { 'path': 'std::option::Option', 'name': 'unwrap_or_default' }, { 'path': 'std::option::Option', 'name': 'get_or_insert_default' }, diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index d1aa840ab08a2..aa8ee86d67247 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -1,50 +1,6 @@ -const QUERY = [ - '

', - '->

', elems: [], foundElems: 0, original: "

", @@ -53,6 +9,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: '->

', elems: [], foundElems: 0, original: "->

", @@ -61,6 +18,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: 'a<"P">', elems: [], foundElems: 0, original: "a<\"P\">", @@ -69,6 +27,7 @@ const PARSED = [ error: "Unexpected `\"` in generics", }, { + query: '"P" "P"', elems: [], foundElems: 0, original: "\"P\" \"P\"", @@ -77,6 +36,7 @@ const PARSED = [ error: "Cannot have more than one literal search element", }, { + query: 'P "P"', elems: [], foundElems: 0, original: "P \"P\"", @@ -85,6 +45,7 @@ const PARSED = [ error: "Cannot use literal search when there is more than one element", }, { + query: '"p" p', elems: [], foundElems: 0, original: "\"p\" p", @@ -93,6 +54,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: '"const": p', elems: [], foundElems: 0, original: "\"const\": p", @@ -101,6 +63,7 @@ const PARSED = [ error: "You cannot use quotes on type filter", }, { + query: "a<:a>", elems: [], foundElems: 0, original: "a<:a>", @@ -109,6 +72,7 @@ const PARSED = [ error: "Expected type filter before `:`", }, { + query: "a<::a>", elems: [], foundElems: 0, original: "a<::a>", @@ -117,6 +81,7 @@ const PARSED = [ error: "Unexpected `::`: paths cannot start with `::`", }, { + query: "((a))", elems: [], foundElems: 0, original: "((a))", @@ -125,6 +90,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "(p -> p", elems: [], foundElems: 0, original: "(p -> p", @@ -133,6 +99,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "::a::b", elems: [], foundElems: 0, original: "::a::b", @@ -141,6 +108,7 @@ const PARSED = [ error: "Paths cannot start with `::`", }, { + query: "a::::b", elems: [], foundElems: 0, original: "a::::b", @@ -149,6 +117,7 @@ const PARSED = [ error: "Unexpected `::::`", }, { + query: "a::b::", elems: [], foundElems: 0, original: "a::b::", @@ -157,6 +126,7 @@ const PARSED = [ error: "Paths cannot end with `::`", }, { + query: ":a", elems: [], foundElems: 0, original: ":a", @@ -165,6 +135,7 @@ const PARSED = [ error: "Expected type filter before `:`", }, { + query: "a b:", elems: [], foundElems: 0, original: "a b:", @@ -173,6 +144,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: "a (b:", elems: [], foundElems: 0, original: "a (b:", @@ -181,6 +153,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "_:", elems: [], foundElems: 0, original: "_:", @@ -189,6 +162,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: "_:a", elems: [], foundElems: 0, original: "_:a", @@ -197,6 +171,7 @@ const PARSED = [ error: "Unknown type filter `_`", }, { + query: "a-bb", elems: [], foundElems: 0, original: "a-bb", @@ -205,6 +180,7 @@ const PARSED = [ error: "Unexpected `-` (did you mean `->`?)", }, { + query: "a>bb", elems: [], foundElems: 0, original: "a>bb", @@ -213,6 +189,7 @@ const PARSED = [ error: "Unexpected `>` (did you mean `->`?)", }, { + query: "ab'", elems: [], foundElems: 0, original: "ab'", @@ -221,6 +198,7 @@ const PARSED = [ error: "Unexpected `'`", }, { + query: "a->", elems: [], foundElems: 0, original: "a->", @@ -229,6 +207,7 @@ const PARSED = [ error: "Expected at least one item after `->`", }, { + query: '"p" ', elems: [], foundElems: 0, original: '"p" ', @@ -237,6 +216,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: '"p" a', elems: [], foundElems: 0, original: '"p" a', @@ -245,6 +225,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: "a,<", elems: [], foundElems: 0, original: 'a,<', @@ -253,6 +234,7 @@ const PARSED = [ error: 'Found generics without a path', }, { + query: "aaaaa<>b", elems: [], foundElems: 0, original: 'aaaaa<>b', @@ -261,6 +243,7 @@ const PARSED = [ error: 'Expected `,`, ` `, `:` or `->`, found `b`', }, { + query: "fn:aaaaa<>b", elems: [], foundElems: 0, original: 'fn:aaaaa<>b', @@ -269,6 +252,7 @@ const PARSED = [ error: 'Expected `,`, ` `, `:` or `->`, found `b`', }, { + query: "->a<>b", elems: [], foundElems: 0, original: '->a<>b', @@ -277,6 +261,7 @@ const PARSED = [ error: 'Expected `,` or ` `, found `b`', }, { + query: "a<->", elems: [], foundElems: 0, original: 'a<->', @@ -285,6 +270,7 @@ const PARSED = [ error: 'Unexpected `-` after `<`', }, { + query: "a:: a", elems: [], foundElems: 0, original: 'a:: a', @@ -293,6 +279,7 @@ const PARSED = [ error: 'Paths cannot end with `::`', }, { + query: "a ::a", elems: [], foundElems: 0, original: 'a ::a', @@ -301,6 +288,7 @@ const PARSED = [ error: 'Paths cannot start with `::`', }, { + query: "a:", elems: [], foundElems: 0, original: "a:", @@ -309,6 +297,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "a<>:", elems: [], foundElems: 0, original: "a<>:", @@ -317,6 +306,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "a,:", elems: [], foundElems: 0, original: "a,:", @@ -325,6 +315,7 @@ const PARSED = [ error: 'Unexpected `,` in type filter', }, { + query: " a<> :", elems: [], foundElems: 0, original: "a<> :", @@ -333,6 +324,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "mod : :", elems: [], foundElems: 0, original: "mod : :", @@ -341,6 +333,7 @@ const PARSED = [ error: 'Unexpected `:`', }, { + query: "a!a", elems: [], foundElems: 0, original: "a!a", @@ -349,6 +342,7 @@ const PARSED = [ error: 'Unexpected `!`: it can only be at the end of an ident', }, { + query: "a!!", elems: [], foundElems: 0, original: "a!!", @@ -357,6 +351,7 @@ const PARSED = [ error: 'Cannot have more than one `!` in an ident', }, { + query: "mod:a!", elems: [], foundElems: 0, original: "mod:a!", @@ -365,6 +360,7 @@ const PARSED = [ error: 'Invalid search type: macro `!` and `mod` both specified', }, { + query: "a!::a", elems: [], foundElems: 0, original: "a!::a", @@ -373,6 +369,7 @@ const PARSED = [ error: 'Cannot have associated items in macros', }, { + query: "a<", elems: [], foundElems: 0, original: "a<", diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index e23447ab75dc4..6f5d66e57ba0c 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -1,17 +1,6 @@ -const QUERY = [ - 'fn:foo', - 'enum : foo', - 'macro:foo', - 'macro!', - 'macro:mac!', - 'a::mac!', - '-> fn:foo', - '-> fn:foo', - '-> fn:foo', -]; - const PARSED = [ { + query: 'fn:foo', elems: [{ name: "foo", fullPath: ["foo"], @@ -27,6 +16,7 @@ const PARSED = [ error: null, }, { + query: 'enum : foo', elems: [{ name: "foo", fullPath: ["foo"], @@ -42,6 +32,7 @@ const PARSED = [ error: null, }, { + query: 'macro:foo', elems: [], foundElems: 0, original: "macro:foo", @@ -50,6 +41,7 @@ const PARSED = [ error: "Unexpected `<` in type filter", }, { + query: 'macro!', elems: [{ name: "macro", fullPath: ["macro"], @@ -65,6 +57,7 @@ const PARSED = [ error: null, }, { + query: 'macro:mac!', elems: [{ name: "mac", fullPath: ["mac"], @@ -80,6 +73,7 @@ const PARSED = [ error: null, }, { + query: 'a::mac!', elems: [{ name: "a::mac", fullPath: ["a", "mac"], @@ -95,6 +89,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", @@ -110,6 +105,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", @@ -134,6 +130,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js index 5a2266dbe3697..eb3cf45515576 100644 --- a/tests/rustdoc-js-std/parser-generics.js +++ b/tests/rustdoc-js-std/parser-generics.js @@ -1,14 +1,6 @@ -const QUERY = [ - 'A, E>', - 'p<> u8', - '"p"', - 'p>', - 'p, r>', - 'p>', -]; - const PARSED = [ { + query: 'A, E>', elems: [], foundElems: 0, original: 'A, E>', @@ -17,6 +9,7 @@ const PARSED = [ error: 'Unclosed `<`', }, { + query: 'p<> u8', elems: [ { name: "p", @@ -42,6 +35,7 @@ const PARSED = [ error: null, }, { + query: '"p"', elems: [ { name: "p", @@ -67,6 +61,7 @@ const PARSED = [ error: null, }, { + query: 'p>', elems: [ { name: "p", @@ -100,6 +95,7 @@ const PARSED = [ error: null, }, { + query: 'p, r>', elems: [ { name: "p", @@ -140,6 +136,7 @@ const PARSED = [ error: null, }, { + query: 'p>', elems: [ { name: "p", diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index be42b7aa46307..d9ee5fb564b68 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -1,14 +1,6 @@ -const QUERY = [ - "R", - "!", - "a!", - "a!::b", - "!::b", - "a!::b!", -]; - const PARSED = [ { + query: "R", elems: [{ name: "r", fullPath: ["r"], @@ -32,6 +24,7 @@ const PARSED = [ error: null, }, { + query: "!", elems: [{ name: "!", fullPath: ["!"], @@ -47,6 +40,7 @@ const PARSED = [ error: null, }, { + query: "a!", elems: [{ name: "a", fullPath: ["a"], @@ -62,6 +56,7 @@ const PARSED = [ error: null, }, { + query: "a!::b", elems: [], foundElems: 0, original: "a!::b", @@ -70,6 +65,7 @@ const PARSED = [ error: "Cannot have associated items in macros", }, { + query: "!::b", elems: [{ name: "!::b", fullPath: ["!", "b"], @@ -85,6 +81,7 @@ const PARSED = [ error: null, }, { + query: "a!::b!", elems: [], foundElems: 0, original: "a!::b!", diff --git a/tests/rustdoc-js-std/parser-literal.js b/tests/rustdoc-js-std/parser-literal.js index 3a31d1bddfff5..87c06224dbf2d 100644 --- a/tests/rustdoc-js-std/parser-literal.js +++ b/tests/rustdoc-js-std/parser-literal.js @@ -1,7 +1,6 @@ -const QUERY = ['R

']; - const PARSED = [ { + query: 'R

', elems: [{ name: "r", fullPath: ["r"], diff --git a/tests/rustdoc-js-std/parser-paths.js b/tests/rustdoc-js-std/parser-paths.js index f3e421f5ffa50..8d4dedf3f46c8 100644 --- a/tests/rustdoc-js-std/parser-paths.js +++ b/tests/rustdoc-js-std/parser-paths.js @@ -1,7 +1,6 @@ -const QUERY = ['A::B', 'A::B,C', 'A::B,C', 'mod::a']; - const PARSED = [ { + query: 'A::B', elems: [{ name: "a::b", fullPath: ["a", "b"], @@ -17,6 +16,7 @@ const PARSED = [ error: null, }, { + query: 'A::B,C', elems: [ { name: "a::b", @@ -42,6 +42,7 @@ const PARSED = [ error: null, }, { + query: 'A::B,C', elems: [ { name: "a::b", @@ -75,6 +76,7 @@ const PARSED = [ error: null, }, { + query: 'mod::a', elems: [{ name: "mod::a", fullPath: ["mod", "a"], diff --git a/tests/rustdoc-js-std/parser-quote.js b/tests/rustdoc-js-std/parser-quote.js index d5d67cac892f5..9d2a3620ed7a3 100644 --- a/tests/rustdoc-js-std/parser-quote.js +++ b/tests/rustdoc-js-std/parser-quote.js @@ -1,15 +1,6 @@ -const QUERY = [ - '-> "p"', - '"p",', - '"p" -> a', - '"a" -> "p"', - '->"-"', - '"a', - '""', -]; - const PARSED = [ { + query: '-> "p"', elems: [], foundElems: 1, original: '-> "p"', @@ -25,6 +16,7 @@ const PARSED = [ error: null, }, { + query: '"p",', elems: [{ name: "p", fullPath: ["p"], @@ -40,6 +32,7 @@ const PARSED = [ error: null, }, { + query: '"p" -> a', elems: [], foundElems: 0, original: '"p" -> a', @@ -48,6 +41,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: '"a" -> "p"', elems: [], foundElems: 0, original: '"a" -> "p"', @@ -56,6 +50,7 @@ const PARSED = [ error: "Cannot have more than one literal search element", }, { + query: '->"-"', elems: [], foundElems: 0, original: '->"-"', @@ -64,6 +59,7 @@ const PARSED = [ error: 'Unexpected `-` in a string element', }, { + query: '"a', elems: [], foundElems: 0, original: '"a', @@ -72,6 +68,7 @@ const PARSED = [ error: 'Unclosed `"`', }, { + query: '""', elems: [], foundElems: 0, original: '""', diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js index c2981319055db..665e2a9b2e3d7 100644 --- a/tests/rustdoc-js-std/parser-returned.js +++ b/tests/rustdoc-js-std/parser-returned.js @@ -1,13 +1,6 @@ -const QUERY = [ - "-> F

", - "-> P", - "->,a", - "aaaaa->a", - "-> !", -]; - const PARSED = [ { + query: "-> F

", elems: [], foundElems: 1, original: "-> F

", @@ -31,6 +24,7 @@ const PARSED = [ error: null, }, { + query: "-> P", elems: [], foundElems: 1, original: "-> P", @@ -46,6 +40,7 @@ const PARSED = [ error: null, }, { + query: "->,a", elems: [], foundElems: 1, original: "->,a", @@ -61,6 +56,7 @@ const PARSED = [ error: null, }, { + query: "aaaaa->a", elems: [{ name: "aaaaa", fullPath: ["aaaaa"], @@ -83,6 +79,7 @@ const PARSED = [ error: null, }, { + query: "-> !", elems: [], foundElems: 1, original: "-> !", diff --git a/tests/rustdoc-js-std/parser-separators.js b/tests/rustdoc-js-std/parser-separators.js index fc8c5114c4e96..69f9ac29ad3ce 100644 --- a/tests/rustdoc-js-std/parser-separators.js +++ b/tests/rustdoc-js-std/parser-separators.js @@ -1,17 +1,8 @@ // ignore-tidy-tab -const QUERY = [ - 'aaaaaa b', - 'a b', - 'a,b', - 'a\tb', - 'a', - 'a', - 'a', -]; - const PARSED = [ { + query: 'aaaaaa b', elems: [ { name: 'aaaaaa', @@ -37,6 +28,7 @@ const PARSED = [ error: null, }, { + query: 'a b', elems: [ { name: 'a', @@ -62,6 +54,7 @@ const PARSED = [ error: null, }, { + query: 'a,b', elems: [ { name: 'a', @@ -87,6 +80,7 @@ const PARSED = [ error: null, }, { + query: 'a\tb', elems: [ { name: 'a', @@ -112,6 +106,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', @@ -144,6 +139,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', @@ -176,6 +172,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index dc1049a70bc38..0e08eaf73c876 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -1,18 +1,10 @@ // This test is mostly to check that the parser still kinda outputs something // (and doesn't enter an infinite loop!) even though the query is completely // invalid. -const QUERY = [ - 'a b', - 'a b', - 'a,b(c)', - 'aaa,a', - ',,,,', - 'mod :', - 'mod\t:', -]; const PARSED = [ { + query: 'a b', elems: [ { name: "a", @@ -38,6 +30,7 @@ const PARSED = [ error: null, }, { + query: 'a b', elems: [ { name: "a", @@ -63,6 +56,7 @@ const PARSED = [ error: null, }, { + query: 'a,b(c)', elems: [], foundElems: 0, original: "a,b(c)", @@ -71,6 +65,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: 'aaa,a', elems: [ { name: "aaa", @@ -96,6 +91,7 @@ const PARSED = [ error: null, }, { + query: ',,,,', elems: [], foundElems: 0, original: ",,,,", @@ -104,6 +100,7 @@ const PARSED = [ error: null, }, { + query: 'mod :', elems: [], foundElems: 0, original: 'mod :', @@ -112,6 +109,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: 'mod\t:', elems: [], foundElems: 0, original: 'mod\t:', diff --git a/tests/rustdoc-js-std/path-ordering.js b/tests/rustdoc-js-std/path-ordering.js index 7dcdd40231248..c3d61d238cc35 100644 --- a/tests/rustdoc-js-std/path-ordering.js +++ b/tests/rustdoc-js-std/path-ordering.js @@ -1,7 +1,6 @@ -const QUERY = 'hashset::insert'; - const EXPECTED = { - 'others': [ + query: 'hashset::insert', + others: [ // ensure hashset::insert comes first { 'path': 'std::collections::hash_set::HashSet', 'name': 'insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert' }, diff --git a/tests/rustdoc-js-std/primitive.js b/tests/rustdoc-js-std/primitive.js index e5690383e4f0b..737e429bf5514 100644 --- a/tests/rustdoc-js-std/primitive.js +++ b/tests/rustdoc-js-std/primitive.js @@ -1,15 +1,6 @@ -const QUERY = [ - 'i8', - 'u32', - 'str', - 'char', - 'unit', - 'tuple', - 'fn', -]; - const EXPECTED = [ { + 'query': 'i8', 'others': [ { 'path': 'std', @@ -19,6 +10,7 @@ const EXPECTED = [ ] }, { + 'query': 'u32', 'others': [ { 'path': 'std', @@ -28,6 +20,7 @@ const EXPECTED = [ ] }, { + 'query': 'str', 'others': [ { 'path': 'std', @@ -37,6 +30,7 @@ const EXPECTED = [ ] }, { + 'query': 'char', 'others': [ { 'path': 'std', @@ -46,6 +40,7 @@ const EXPECTED = [ ] }, { + 'query': 'unit', 'others': [ { 'path': 'std', @@ -55,6 +50,7 @@ const EXPECTED = [ ] }, { + 'query': 'tuple', 'others': [ { 'path': 'std', @@ -64,6 +60,7 @@ const EXPECTED = [ ] }, { + 'query': 'fn', 'others': [ { 'path': 'std', diff --git a/tests/rustdoc-js-std/println-typo.js b/tests/rustdoc-js-std/println-typo.js index 7ca3ab8e56333..a4dd90a44d5ba 100644 --- a/tests/rustdoc-js-std/println-typo.js +++ b/tests/rustdoc-js-std/println-typo.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = 'prinltn'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': 'prinltn', 'others': [ { 'path': 'std', 'name': 'println' }, { 'path': 'std', 'name': 'print' }, diff --git a/tests/rustdoc-js-std/quoted.js b/tests/rustdoc-js-std/quoted.js index aec8484a41f6d..8a9275019255c 100644 --- a/tests/rustdoc-js-std/quoted.js +++ b/tests/rustdoc-js-std/quoted.js @@ -1,9 +1,9 @@ // ignore-order -const QUERY = '"error"'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': '"error"', 'others': [ { 'path': 'std', 'name': 'error' }, { 'path': 'std::fmt', 'name': 'Error' }, diff --git a/tests/rustdoc-js-std/reference-shrink.js b/tests/rustdoc-js-std/reference-shrink.js index f90be6d1bfd35..b602bbdca188d 100644 --- a/tests/rustdoc-js-std/reference-shrink.js +++ b/tests/rustdoc-js-std/reference-shrink.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'reference::shrink'; - const EXPECTED = { + 'query': 'reference::shrink', // avoid including the method that's not going to be in the HTML 'others': [], }; diff --git a/tests/rustdoc-js-std/regex.js b/tests/rustdoc-js-std/regex.js index a6843c595f7ad..7dc38939a17d4 100644 --- a/tests/rustdoc-js-std/regex.js +++ b/tests/rustdoc-js-std/regex.js @@ -1,9 +1,8 @@ // exact-check // https://github.com/rust-lang/rust/issues/103357 -const QUERY = 'regex'; - const EXPECTED = { + 'query': 'regex', 'others': [], 'in_args': [], 'returned': [], diff --git a/tests/rustdoc-js-std/return-specific-literal.js b/tests/rustdoc-js-std/return-specific-literal.js index c7c347240b751..86ed3aceb4e84 100644 --- a/tests/rustdoc-js-std/return-specific-literal.js +++ b/tests/rustdoc-js-std/return-specific-literal.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:"string"'; - const EXPECTED = { + 'query': 'struct:"string"', 'in_args': [ { 'path': 'std::string::String', 'name': 'ne' }, ], diff --git a/tests/rustdoc-js-std/return-specific.js b/tests/rustdoc-js-std/return-specific.js index d9a910553b8de..be54a1c977254 100644 --- a/tests/rustdoc-js-std/return-specific.js +++ b/tests/rustdoc-js-std/return-specific.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:string'; - const EXPECTED = { + 'query': 'struct:string', 'in_args': [ { 'path': 'std::string::String', 'name': 'ne' }, ], diff --git a/tests/rustdoc-js-std/should-fail.js b/tests/rustdoc-js-std/should-fail.js index b85a47dc08a88..94f82efd9b497 100644 --- a/tests/rustdoc-js-std/should-fail.js +++ b/tests/rustdoc-js-std/should-fail.js @@ -1,8 +1,7 @@ // should-fail -const QUERY = 'fn'; - const EXPECTED = { + 'query': 'fn', 'others': [ { 'path': 'std', 'name': 'fn', ty: 14 }, ], diff --git a/tests/rustdoc-js-std/string-from_ut.js b/tests/rustdoc-js-std/string-from_ut.js index f9edf4408db85..1fff6ee28bb42 100644 --- a/tests/rustdoc-js-std/string-from_ut.js +++ b/tests/rustdoc-js-std/string-from_ut.js @@ -1,6 +1,5 @@ -const QUERY = 'String::from_ut'; - const EXPECTED = { + 'query': 'String::from_ut', 'others': [ { 'path': 'std::string::String', 'name': 'from_utf8' }, { 'path': 'std::string::String', 'name': 'from_utf8' }, diff --git a/tests/rustdoc-js-std/struct-vec.js b/tests/rustdoc-js-std/struct-vec.js index 29609904b1957..dd72aaa1ab86c 100644 --- a/tests/rustdoc-js-std/struct-vec.js +++ b/tests/rustdoc-js-std/struct-vec.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:VecD'; - const EXPECTED = { + 'query': 'struct:VecD', 'others': [ { 'path': 'std::collections', 'name': 'VecDeque' }, { 'path': 'std::vec', 'name': 'Vec' }, diff --git a/tests/rustdoc-js-std/typed-query.js b/tests/rustdoc-js-std/typed-query.js index eeb3e18886959..8e84645889adf 100644 --- a/tests/rustdoc-js-std/typed-query.js +++ b/tests/rustdoc-js-std/typed-query.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = 'macro:print'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': 'macro:print', 'others': [ { 'path': 'std', 'name': 'print' }, { 'path': 'std', 'name': 'println' }, diff --git a/tests/rustdoc-js-std/vec-new.js b/tests/rustdoc-js-std/vec-new.js index fc44a566af21f..309f3543faffe 100644 --- a/tests/rustdoc-js-std/vec-new.js +++ b/tests/rustdoc-js-std/vec-new.js @@ -1,6 +1,5 @@ -const QUERY = 'Vec::new'; - const EXPECTED = { + 'query': 'Vec::new', 'others': [ { 'path': 'std::vec::Vec', 'name': 'new' }, { 'path': 'alloc::vec::Vec', 'name': 'new' }, diff --git a/tests/rustdoc-js/basic.js b/tests/rustdoc-js/basic.js index d99b23468b60c..e186d510887cb 100644 --- a/tests/rustdoc-js/basic.js +++ b/tests/rustdoc-js/basic.js @@ -1,6 +1,5 @@ -const QUERY = 'Fo'; - const EXPECTED = { + 'query': 'Fo', 'others': [ { 'path': 'basic', 'name': 'Foo' }, ], diff --git a/tests/rustdoc-js/doc-alias-filter-out.js b/tests/rustdoc-js/doc-alias-filter-out.js index 46a089d06ebef..fd25370dff3c1 100644 --- a/tests/rustdoc-js/doc-alias-filter-out.js +++ b/tests/rustdoc-js/doc-alias-filter-out.js @@ -1,9 +1,8 @@ // exact-check -const QUERY = 'true'; - const FILTER_CRATE = 'some_other_crate'; const EXPECTED = { + 'query': 'true', 'others': [], }; diff --git a/tests/rustdoc-js/doc-alias-filter.js b/tests/rustdoc-js/doc-alias-filter.js index e06047ba7606e..1d2dd8b9a8cde 100644 --- a/tests/rustdoc-js/doc-alias-filter.js +++ b/tests/rustdoc-js/doc-alias-filter.js @@ -1,10 +1,9 @@ // exact-check -const QUERY = '"true"'; - const FILTER_CRATE = 'doc_alias_filter'; const EXPECTED = { + 'query': '"true"', 'others': [ { 'path': 'doc_alias_filter', diff --git a/tests/rustdoc-js/doc-alias-whitespace.js b/tests/rustdoc-js/doc-alias-whitespace.js index c9fc0c4311f19..64784b5698be9 100644 --- a/tests/rustdoc-js/doc-alias-whitespace.js +++ b/tests/rustdoc-js/doc-alias-whitespace.js @@ -1,11 +1,8 @@ // exact-check -const QUERY = [ - 'Demon Lord', -]; - const EXPECTED = [ { + 'query': 'Demon Lord', 'others': [ { 'path': 'doc_alias_whitespace', diff --git a/tests/rustdoc-js/doc-alias.js b/tests/rustdoc-js/doc-alias.js index 62c8e7a74b940..7e4e8a776d899 100644 --- a/tests/rustdoc-js/doc-alias.js +++ b/tests/rustdoc-js/doc-alias.js @@ -1,31 +1,6 @@ -const QUERY = [ - 'StructItem', - 'StructFieldItem', - 'StructMethodItem', - 'ImplTraitItem', - 'StructImplConstItem', - 'ImplTraitFunction', - 'EnumItem', - 'VariantItem', - 'EnumMethodItem', - 'TypedefItem', - 'TraitItem', - 'TraitTypeItem', - 'AssociatedConstItem', - 'TraitFunctionItem', - 'FunctionItem', - 'ModuleItem', - 'ConstItem', - 'StaticItem', - 'UnionItem', - 'UnionFieldItem', - 'UnionMethodItem', - 'MacroItem', -]; - const EXPECTED = [ { - // StructItem + 'query': 'StructItem', 'others': [ { 'path': 'doc_alias', @@ -37,7 +12,7 @@ const EXPECTED = [ ], }, { - // StructFieldItem + 'query': 'StructFieldItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -49,7 +24,7 @@ const EXPECTED = [ ], }, { - // StructMethodItem + 'query': 'StructMethodItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -61,11 +36,11 @@ const EXPECTED = [ ], }, { - // ImplTraitItem + 'query': 'ImplTraitItem', 'others': [], }, { - // StructImplConstItem + 'query': 'StructImplConstItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -77,7 +52,7 @@ const EXPECTED = [ ], }, { - // ImplTraitFunction + 'query': 'ImplTraitFunction', 'others': [ { 'path': 'doc_alias::Struct', @@ -89,7 +64,7 @@ const EXPECTED = [ ], }, { - // EnumItem + 'query': 'EnumItem', 'others': [ { 'path': 'doc_alias', @@ -101,7 +76,7 @@ const EXPECTED = [ ], }, { - // VariantItem + 'query': 'VariantItem', 'others': [ { 'path': 'doc_alias::Enum', @@ -113,7 +88,7 @@ const EXPECTED = [ ], }, { - // EnumMethodItem + 'query': 'EnumMethodItem', 'others': [ { 'path': 'doc_alias::Enum', @@ -125,7 +100,7 @@ const EXPECTED = [ ], }, { - // TypedefItem + 'query': 'TypedefItem', 'others': [ { 'path': 'doc_alias', @@ -137,7 +112,7 @@ const EXPECTED = [ ], }, { - // TraitItem + 'query': 'TraitItem', 'others': [ { 'path': 'doc_alias', @@ -149,7 +124,7 @@ const EXPECTED = [ ], }, { - // TraitTypeItem + 'query': 'TraitTypeItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -161,7 +136,7 @@ const EXPECTED = [ ], }, { - // AssociatedConstItem + 'query': 'AssociatedConstItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -173,7 +148,7 @@ const EXPECTED = [ ], }, { - // TraitFunctionItem + 'query': 'TraitFunctionItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -185,7 +160,7 @@ const EXPECTED = [ ], }, { - // FunctionItem + 'query': 'FunctionItem', 'others': [ { 'path': 'doc_alias', @@ -197,7 +172,7 @@ const EXPECTED = [ ], }, { - // ModuleItem + 'query': 'ModuleItem', 'others': [ { 'path': 'doc_alias', @@ -209,7 +184,7 @@ const EXPECTED = [ ], }, { - // ConstItem + 'query': 'ConstItem', 'others': [ { 'path': 'doc_alias', @@ -225,7 +200,7 @@ const EXPECTED = [ ], }, { - // StaticItem + 'query': 'StaticItem', 'others': [ { 'path': 'doc_alias', @@ -237,7 +212,7 @@ const EXPECTED = [ ], }, { - // UnionItem + 'query': 'UnionItem', 'others': [ { 'path': 'doc_alias', @@ -255,7 +230,7 @@ const EXPECTED = [ ], }, { - // UnionFieldItem + 'query': 'UnionFieldItem', 'others': [ { 'path': 'doc_alias::Union', @@ -267,7 +242,7 @@ const EXPECTED = [ ], }, { - // UnionMethodItem + 'query': 'UnionMethodItem', 'others': [ { 'path': 'doc_alias::Union', @@ -279,7 +254,7 @@ const EXPECTED = [ ], }, { - // MacroItem + 'query': 'MacroItem', 'others': [ { 'path': 'doc_alias', diff --git a/tests/rustdoc-js/exact-match.js b/tests/rustdoc-js/exact-match.js index b0a411bee5829..ce3a76f9b7dd3 100644 --- a/tests/rustdoc-js/exact-match.js +++ b/tests/rustdoc-js/exact-match.js @@ -1,6 +1,5 @@ -const QUERY = 'si::pc'; - const EXPECTED = { + 'query': 'si::pc', 'others': [ { 'path': 'exact_match::Si', 'name': 'pc' }, { 'path': 'exact_match::Psi', 'name': 'pc' }, diff --git a/tests/rustdoc-js/foreign-type-path.js b/tests/rustdoc-js/foreign-type-path.js index 334761badcab1..b11123d3ed9b1 100644 --- a/tests/rustdoc-js/foreign-type-path.js +++ b/tests/rustdoc-js/foreign-type-path.js @@ -1,6 +1,5 @@ -const QUERY = 'MyForeignType::my_method'; - const EXPECTED = { + 'query': 'MyForeignType::my_method', 'others': [ // Test case for https://github.com/rust-lang/rust/pull/96887#pullrequestreview-967154358 // Validates that the parent path for a foreign type method is correct. diff --git a/tests/rustdoc-js/generics-impl.js b/tests/rustdoc-js/generics-impl.js index 5051743bda2d1..5e33e224876fe 100644 --- a/tests/rustdoc-js/generics-impl.js +++ b/tests/rustdoc-js/generics-impl.js @@ -1,68 +1,56 @@ // exact-check -const QUERY = [ - 'Aaaaaaa -> u32', - 'Aaaaaaa -> bool', - 'Aaaaaaa -> usize', - 'Read -> u64', - 'trait:Read -> u64', - 'struct:Read -> u64', - 'bool -> u64', - 'Ddddddd -> u64', - '-> Ddddddd' -]; - const EXPECTED = [ { - // Aaaaaaa -> u32 + 'query': 'Aaaaaaa -> u32', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'bbbbbbb' }, ], }, { - // Aaaaaaa -> bool + 'query': 'Aaaaaaa -> bool', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'ccccccc' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'read' }, ], }, { - // Read -> u64 + 'query': 'Read -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' }, { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // trait:Read -> u64 + 'query': 'trait:Read -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' }, { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // struct:Read -> u64 + 'query': 'struct:Read -> u64', 'others': [], }, { - // bool -> u64 + 'query': 'bool -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'fffffff' }, ], }, { - // Ddddddd -> u64 + 'query': 'Ddddddd -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // -> Ddddddd + 'query': '-> Ddddddd', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'hhhhhhh' }, ], diff --git a/tests/rustdoc-js/generics-multi-trait.js b/tests/rustdoc-js/generics-multi-trait.js index e7fcea876c85c..7097cabe7a21f 100644 --- a/tests/rustdoc-js/generics-multi-trait.js +++ b/tests/rustdoc-js/generics-multi-trait.js @@ -1,14 +1,9 @@ // exact-check -const QUERY = [ - 'Result', - 'Zzzzzzzzzzzzzzzzzz', - 'Nonononononononono', -]; - const EXPECTED = [ // check one of the generic items { + 'query': 'Result', 'in_args': [ { 'path': 'generics_multi_trait', 'name': 'beta' }, ], @@ -17,6 +12,7 @@ const EXPECTED = [ ], }, { + 'query': 'Zzzzzzzzzzzzzzzzzz', 'in_args': [ { 'path': 'generics_multi_trait', 'name': 'beta' }, ], @@ -26,6 +22,7 @@ const EXPECTED = [ }, // ignore the name of the generic itself { + 'query': 'Nonononononononono', 'in_args': [], 'returned': [], }, diff --git a/tests/rustdoc-js/generics-nested.js b/tests/rustdoc-js/generics-nested.js index 8701f2d49861a..294c194907489 100644 --- a/tests/rustdoc-js/generics-nested.js +++ b/tests/rustdoc-js/generics-nested.js @@ -1,31 +1,24 @@ // exact-check -const QUERY = [ - '-> Out>', - '-> Out>', - '-> Out', - '-> Out', -]; - const EXPECTED = [ { - // -> Out> + 'query': '-> Out>', 'others': [ { 'path': 'generics_nested', 'name': 'alef' }, ], }, { - // -> Out> + 'query': '-> Out>', 'others': [], }, { - // -> Out + 'query': '-> Out', 'others': [ { 'path': 'generics_nested', 'name': 'bet' }, ], }, { - // -> Out + 'query': '-> Out', 'others': [ { 'path': 'generics_nested', 'name': 'bet' }, ], diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js index 0e84751603ed6..4ccfb8f4e4d02 100644 --- a/tests/rustdoc-js/generics-trait.js +++ b/tests/rustdoc-js/generics-trait.js @@ -1,22 +1,9 @@ // exact-check -const QUERY = [ - 'Result', - 'Result', - 'OtherThingxxxxxxxx', - 'OtherThingxxxxxxxy', -]; - -const CORRECTIONS = [ - null, - null, - null, - 'OtherThingxxxxxxxx', -]; - const EXPECTED = [ - // Result { + 'query': 'Result', + 'correction': null, 'in_args': [ { 'path': 'generics_trait', 'name': 'beta' }, ], @@ -24,13 +11,15 @@ const EXPECTED = [ { 'path': 'generics_trait', 'name': 'bet' }, ], }, - // Result { + 'query': 'Result', + 'correction': null, 'in_args': [], 'returned': [], }, - // OtherThingxxxxxxxx { + 'query': 'OtherThingxxxxxxxx', + 'correction': null, 'in_args': [ { 'path': 'generics_trait', 'name': 'alpha' }, ], @@ -38,8 +27,9 @@ const EXPECTED = [ { 'path': 'generics_trait', 'name': 'alef' }, ], }, - // OtherThingxxxxxxxy { + 'query': 'OtherThingxxxxxxxy', + 'correction': 'OtherThingxxxxxxxx', 'in_args': [ { 'path': 'generics_trait', 'name': 'alpha' }, ], diff --git a/tests/rustdoc-js/generics.js b/tests/rustdoc-js/generics.js index f79c709ad6cf0..ebc92ccfc0575 100644 --- a/tests/rustdoc-js/generics.js +++ b/tests/rustdoc-js/generics.js @@ -1,20 +1,8 @@ // exact-check -const QUERY = [ - 'R

', - 'R', - 'R', - '"P"', - 'P', - 'ExtraCreditStructMulti', - 'TraitCat', - 'TraitDog', - 'Result', -]; - const EXPECTED = [ { - // R

+ 'query': 'R

', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -23,7 +11,7 @@ const EXPECTED = [ ], }, { - // R + 'query': 'R', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -32,12 +20,12 @@ const EXPECTED = [ ], }, { - // R + 'query': 'R', 'returned': [], 'in_args': [], }, { - // "P" + 'query': '"P"', 'others': [ { 'path': 'generics', 'name': 'P' }, ], @@ -49,7 +37,7 @@ const EXPECTED = [ ], }, { - // P + 'query': 'P', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -58,26 +46,26 @@ const EXPECTED = [ ], }, { - // "ExtraCreditStructMulti" + 'query': '"ExtraCreditStructMulti"', 'in_args': [ { 'path': 'generics', 'name': 'extracreditlabhomework' }, ], 'returned': [], }, { - // TraitCat + 'query': 'TraitCat', 'in_args': [ { 'path': 'generics', 'name': 'gamma' }, ], }, { - // TraitDog + 'query': 'TraitDog', 'in_args': [ { 'path': 'generics', 'name': 'gamma' }, ], }, { - // Result + 'query': 'Result', 'others': [], 'returned': [ { 'path': 'generics', 'name': 'super_soup' }, diff --git a/tests/rustdoc-js/impl-trait.js b/tests/rustdoc-js/impl-trait.js index 8d594bf8aea75..710e594b54774 100644 --- a/tests/rustdoc-js/impl-trait.js +++ b/tests/rustdoc-js/impl-trait.js @@ -1,32 +1,24 @@ // ignore-order -const QUERY = [ - 'Aaaaaaa -> i32', - 'Aaaaaaa -> Aaaaaaa', - 'Aaaaaaa -> usize', - '-> Aaaaaaa', - 'Aaaaaaa', -]; - const EXPECTED = [ { - // Aaaaaaa -> i32 + 'query': 'Aaaaaaa -> i32', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'eeeeeee' }, ], }, { - // Aaaaaaa -> Aaaaaaa + 'query': 'Aaaaaaa -> Aaaaaaa', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [], }, { - // -> Aaaaaaa + 'query': '-> Aaaaaaa', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' }, @@ -34,7 +26,7 @@ const EXPECTED = [ ], }, { - // Aaaaaaa + 'query': 'Aaaaaaa', 'others': [ { 'path': 'impl_trait', 'name': 'Aaaaaaa' }, ], diff --git a/tests/rustdoc-js/macro-search.js b/tests/rustdoc-js/macro-search.js index 2b179ce146bf0..241f7f1728859 100644 --- a/tests/rustdoc-js/macro-search.js +++ b/tests/rustdoc-js/macro-search.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'abracadabra!'; - const EXPECTED = { + 'query': 'abracadabra!', 'others': [ { 'path': 'macro_search', 'name': 'abracadabra' }, { 'path': 'macro_search', 'name': 'abracadabra_b' }, diff --git a/tests/rustdoc-js/module-substring.js b/tests/rustdoc-js/module-substring.js index f17a97f13dc7e..7a10397ebc620 100644 --- a/tests/rustdoc-js/module-substring.js +++ b/tests/rustdoc-js/module-substring.js @@ -1,6 +1,5 @@ -const QUERY = 'ig::pc'; - const EXPECTED = { + 'query': 'ig::pc', 'others': [ { 'path': 'module_substring::Sig', 'name': 'pc' }, { 'path': 'module_substring::Si', 'name': 'pc' }, diff --git a/tests/rustdoc-js/path-ordering.js b/tests/rustdoc-js/path-ordering.js index 4aee569b0f481..f2e6fe2fa61c4 100644 --- a/tests/rustdoc-js/path-ordering.js +++ b/tests/rustdoc-js/path-ordering.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'b::ccccccc'; - const EXPECTED = { + 'query': 'b::ccccccc', 'others': [ // `ccccccc` is an exact match for all three of these. // However `b` is a closer match for `bb` than for any diff --git a/tests/rustdoc-js/primitive.js b/tests/rustdoc-js/primitive.js index 4aec98c340379..ad8f6663aad12 100644 --- a/tests/rustdoc-js/primitive.js +++ b/tests/rustdoc-js/primitive.js @@ -1,33 +1,30 @@ // exact-check -const QUERY = [ - "i32", - "str", - "primitive:str", - "struct:str", - "TotoIsSomewhere", -]; - const EXPECTED = [ { + 'query': 'i32', 'in_args': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'str', 'returned': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'primitive:str', 'returned': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'struct:str', 'returned': [], }, { + 'query': 'TotoIsSomewhere', 'others': [], 'in_args': [], 'returned': [], diff --git a/tests/rustdoc-js/prototype.js b/tests/rustdoc-js/prototype.js index 2f1d841c3be19..da72fdce3db93 100644 --- a/tests/rustdoc-js/prototype.js +++ b/tests/rustdoc-js/prototype.js @@ -1,14 +1,14 @@ // exact-check -const QUERY = ['constructor', '__proto__']; - const EXPECTED = [ { + 'query': 'constructor', 'others': [], 'returned': [], 'in_args': [], }, { + 'query': '__proto__', 'others': [], 'returned': [], 'in_args': [], diff --git a/tests/rustdoc-js/raw-pointer.js b/tests/rustdoc-js/raw-pointer.js index 140b955ea713a..f2b1294ee3c86 100644 --- a/tests/rustdoc-js/raw-pointer.js +++ b/tests/rustdoc-js/raw-pointer.js @@ -1,33 +1,25 @@ // ignore-order -const QUERY = [ - 'Aaaaaaa -> i32', - 'Aaaaaaa -> Aaaaaaa', - 'Aaaaaaa -> usize', - '-> Aaaaaaa', - 'Aaaaaaa', -]; - const EXPECTED = [ { - // Aaaaaaa -> i32 + 'query': 'Aaaaaaa -> i32', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'eeeeeee' }, ], }, { - // Aaaaaaa -> Aaaaaaa + 'query': 'Aaaaaaa -> Aaaaaaa', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [], }, { - // -> Aaaaaaa + 'query': '-> Aaaaaaa', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, @@ -36,7 +28,7 @@ const EXPECTED = [ ], }, { - // Aaaaaaa + 'query': 'Aaaaaaa', 'others': [ { 'path': 'raw_pointer', 'name': 'Aaaaaaa' }, ], diff --git a/tests/rustdoc-js/reexport.js b/tests/rustdoc-js/reexport.js index 871e75d9b2b31..9021cc2e90fe0 100644 --- a/tests/rustdoc-js/reexport.js +++ b/tests/rustdoc-js/reexport.js @@ -1,15 +1,15 @@ // exact-check -const QUERY = ['Subscriber', 'AnotherOne']; - const EXPECTED = [ { + 'query': 'Subscriber', 'others': [ { 'path': 'reexport::fmt', 'name': 'Subscriber' }, { 'path': 'reexport', 'name': 'FmtSubscriber' }, ], }, { + 'query': 'AnotherOne', 'others': [ { 'path': 'reexport', 'name': 'AnotherOne' }, ], diff --git a/tests/rustdoc-js/search-bag-semantics.js b/tests/rustdoc-js/search-bag-semantics.js index c56a3df5f904c..4b598cd80cc07 100644 --- a/tests/rustdoc-js/search-bag-semantics.js +++ b/tests/rustdoc-js/search-bag-semantics.js @@ -1,18 +1,15 @@ // exact-check -const QUERY = [ - 'P', - 'P, P', -]; - const EXPECTED = [ { + 'query': 'P', 'in_args': [ { 'path': 'search_bag_semantics', 'name': 'alacazam' }, { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, ], }, { + 'query': 'P, P', 'others': [ { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, ], diff --git a/tests/rustdoc-js/search-short-types.js b/tests/rustdoc-js/search-short-types.js index 3b2f15a40bf87..5048e0443c1c2 100644 --- a/tests/rustdoc-js/search-short-types.js +++ b/tests/rustdoc-js/search-short-types.js @@ -1,6 +1,5 @@ -const QUERY = 'P'; - const EXPECTED = { + 'query': 'P', 'others': [ { 'path': 'search_short_types', 'name': 'P' }, { 'path': 'search_short_types::VeryLongTypeName', 'name': 'p' }, diff --git a/tests/rustdoc-js/slice-array.js b/tests/rustdoc-js/slice-array.js index 8c21e06dc4e4f..1c06566920c2b 100644 --- a/tests/rustdoc-js/slice-array.js +++ b/tests/rustdoc-js/slice-array.js @@ -1,63 +1,52 @@ // exact-check -const QUERY = [ - 'R>', - 'primitive:slice>', - 'R>', - 'primitive:slice>', - 'R>', - 'primitive:array>', - 'primitive:array', - 'primitive:array', -]; - const EXPECTED = [ { - // R> + 'query': 'R>', 'returned': [], 'in_args': [ { 'path': 'slice_array', 'name': 'alpha' }, ], }, { - // primitive:slice> + 'query': 'primitive:slice>', 'returned': [ { 'path': 'slice_array', 'name': 'alef' }, ], 'in_args': [], }, { - // R> + 'query': 'R>', 'returned': [], 'in_args': [], }, { - // primitive:slice> + 'query': 'primitive:slice>', 'returned': [], 'in_args': [], }, { - // R> + 'query': 'R>', 'returned': [ { 'path': 'slice_array', 'name': 'bet' }, ], 'in_args': [], }, { - // primitive:array> + 'query': 'primitive:array>', 'returned': [], 'in_args': [ { 'path': 'slice_array', 'name': 'beta' }, ], }, { - // primitive::array + 'query': 'primitive:array', 'in_args': [ { 'path': 'slice_array', 'name': 'gamma' }, ], }, { - // primitive::array + 'query': 'primitive:array', 'in_args': [ { 'path': 'slice_array', 'name': 'gamma' }, ], diff --git a/tests/rustdoc-js/struct-like-variant.js b/tests/rustdoc-js/struct-like-variant.js index f6deea51e7d4d..7b9bec7aea832 100644 --- a/tests/rustdoc-js/struct-like-variant.js +++ b/tests/rustdoc-js/struct-like-variant.js @@ -1,6 +1,5 @@ -const QUERY = 'name'; - const EXPECTED = { + 'query': 'name', 'others': [ { 'path': 'struct_like_variant::Enum::Bar', 'name': 'name', 'desc': 'This is a name.' }, ], diff --git a/tests/rustdoc-js/substring.js b/tests/rustdoc-js/substring.js index af05cd1ad34d1..96efa992bb685 100644 --- a/tests/rustdoc-js/substring.js +++ b/tests/rustdoc-js/substring.js @@ -1,6 +1,5 @@ -const QUERY = 'waker_from'; - const EXPECTED = { + 'query': 'waker_from', 'others': [ { 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' }, { 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' }, diff --git a/tests/rustdoc-js/summaries.js b/tests/rustdoc-js/summaries.js index dfb11e80414c1..ae3aefb0c48a0 100644 --- a/tests/rustdoc-js/summaries.js +++ b/tests/rustdoc-js/summaries.js @@ -1,19 +1,20 @@ // ignore-tidy-linelength -const QUERY = ['summaries', 'summaries::Sidebar', 'summaries::Sidebar2']; - const EXPECTED = [ { + 'query': 'summaries', 'others': [ { 'path': '', 'name': 'summaries', 'desc': 'This summary has a link, [code], and Sidebar2 intra-doc.' }, ], }, { + 'query': 'summaries::Sidebar', 'others': [ { 'path': 'summaries', 'name': 'Sidebar', 'desc': 'This code will be rendered in a code tag.' }, ], }, { + 'query': 'summaries::Sidebar2', 'others': [ { 'path': 'summaries', 'name': 'Sidebar2', 'desc': '' }, ], diff --git a/tests/rustdoc-js/where-clause.js b/tests/rustdoc-js/where-clause.js index 86254a80e20f3..8dccf197be060 100644 --- a/tests/rustdoc-js/where-clause.js +++ b/tests/rustdoc-js/where-clause.js @@ -1,28 +1,31 @@ -const QUERY = ['trait', '-> trait', 't1, t2', '-> shazam', 'drizzel -> shazam']; - const EXPECTED = [ { + 'query': 'trait', 'in_args': [ { 'path': 'where_clause', 'name': 'abracadabra' }, ], }, { + 'query': '-> trait', 'others': [ { 'path': 'where_clause', 'name': 'alacazam' }, ], }, { + 'query': 't1, t2', 'others': [ { 'path': 'where_clause', 'name': 'presto' }, ], }, { + 'query': '-> shazam', 'others': [ { 'path': 'where_clause', 'name': 'bippety' }, { 'path': 'where_clause::Drizzel', 'name': 'boppety' }, ], }, { + 'query': 'drizzel -> shazam', 'others': [ { 'path': 'where_clause::Drizzel', 'name': 'boppety' }, ], From 3681285df77af155fe0f34c64226f4ea89c7150b Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 20 May 2023 11:28:25 +0200 Subject: [PATCH 796/806] Add diagnostic items for `f32::NAN` and `f64::NAN` --- compiler/rustc_span/src/symbol.rs | 2 ++ library/core/src/num/f32.rs | 1 + library/core/src/num/f64.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2f002e424277e..c5ce2575fff06 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -701,7 +701,9 @@ symbols! { f, f16c_target_feature, f32, + f32_nan, f64, + f64_nan, fabsf32, fabsf64, fadd_fast, diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4a035ad61e107..d050d21c8c575 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -403,6 +403,7 @@ impl f32 { /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[rustc_diagnostic_item = "f32_nan"] pub const NAN: f32 = 0.0_f32 / 0.0_f32; /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3aafc435f1e17..d9a738191f7f1 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -401,6 +401,7 @@ impl f64 { /// This constant isn't guaranteed to equal to any specific NaN bitpattern, /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. + #[rustc_diagnostic_item = "f64_nan"] #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NAN: f64 = 0.0_f64 / 0.0_f64; /// Infinity (∞). From 5bd8ba84931628fc2fa3e1c75cc9b9762c449125 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Sat, 10 Jun 2023 00:06:34 +0800 Subject: [PATCH 797/806] Make "consider importing" consistent for macros --- compiler/rustc_resolve/src/diagnostics.rs | 9 +++++++-- compiler/rustc_resolve/src/lib.rs | 4 +++- compiler/rustc_resolve/src/macros.rs | 6 +++--- tests/ui/empty/empty-macro-use.stderr | 6 ++++-- tests/ui/hygiene/no_implicit_prelude-2018.stderr | 6 ++++-- tests/ui/macros/issue-88228.rs | 5 ++--- tests/ui/macros/issue-88228.stderr | 14 +++++++++----- tests/ui/macros/macro-use-wrong-name.stderr | 12 +++++++++--- tests/ui/missing/missing-macro-use.stderr | 6 ++++-- tests/ui/proc-macro/derive-helper-shadowing.stderr | 12 ++++++++---- 10 files changed, 53 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 15c8a690530e7..ca811c9ed7d98 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1352,6 +1352,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { macro_kind: MacroKind, parent_scope: &ParentScope<'a>, ident: Ident, + krate: &Crate, ) { let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); let suggestion = self.early_lookup_typo_candidate( @@ -1364,13 +1365,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let import_suggestions = self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); + let (span, found_use) = match parent_scope.module.nearest_parent_mod().as_local() { + Some(def_id) => UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]), + None => (None, FoundUse::No), + }; show_candidates( self.tcx, err, - None, + span, &import_suggestions, Instead::No, - FoundUse::Yes, + found_use, DiagnosticMode::Normal, vec![], "", diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index dd8d01e35e5de..db97039b907c4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1522,7 +1522,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.tcx.sess.time("check_hidden_glob_reexports", || { self.check_hidden_glob_reexports(exported_ambiguities) }); - self.tcx.sess.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); + self.tcx + .sess + .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 805c804e5759f..ca4f3331b9a2d 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -7,7 +7,7 @@ use crate::{BuiltinMacroState, Determinacy}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; use rustc_ast::expand::StrippedCfgItem; -use rustc_ast::{self as ast, attr, Inline, ItemKind, ModKind, NodeId}; +use rustc_ast::{self as ast, attr, Crate, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; use rustc_data_structures::intern::Interned; @@ -674,7 +674,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext), res)) } - pub(crate) fn finalize_macro_resolutions(&mut self) { + pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { let check_consistency = |this: &mut Self, path: &[Segment], span, @@ -795,7 +795,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let expected = kind.descr_expected(); let msg = format!("cannot find {} `{}` in this scope", expected, ident); let mut err = self.tcx.sess.struct_span_err(ident.span, msg); - self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident); + self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate); err.emit(); } } diff --git a/tests/ui/empty/empty-macro-use.stderr b/tests/ui/empty/empty-macro-use.stderr index e0b3b8685d6eb..5d552e4c40846 100644 --- a/tests/ui/empty/empty-macro-use.stderr +++ b/tests/ui/empty/empty-macro-use.stderr @@ -4,8 +4,10 @@ error: cannot find macro `macro_two` in this scope LL | macro_two!(); | ^^^^^^^^^ | - = help: consider importing this macro: - two_macros::macro_two +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/hygiene/no_implicit_prelude-2018.stderr b/tests/ui/hygiene/no_implicit_prelude-2018.stderr index 3f31b041b6203..b22f3e75b6f4f 100644 --- a/tests/ui/hygiene/no_implicit_prelude-2018.stderr +++ b/tests/ui/hygiene/no_implicit_prelude-2018.stderr @@ -4,8 +4,10 @@ error: cannot find macro `print` in this scope LL | print!(); | ^^^^^ | - = help: consider importing this macro: - std::print +help: consider importing this macro + | +LL + use std::print; + | error: aborting due to previous error diff --git a/tests/ui/macros/issue-88228.rs b/tests/ui/macros/issue-88228.rs index 60ba2eab7a7bf..ec55a262509aa 100644 --- a/tests/ui/macros/issue-88228.rs +++ b/tests/ui/macros/issue-88228.rs @@ -1,14 +1,14 @@ // compile-flags: -Z deduplicate-diagnostics=yes // edition:2018 -mod hey { +mod hey { //~ HELP consider importing this derive macro + //~^ HELP consider importing this macro pub use Copy as Bla; pub use std::println as bla; } #[derive(Bla)] //~^ ERROR cannot find derive macro `Bla` -//~| HELP consider importing this derive macro struct A; #[derive(println)] @@ -19,5 +19,4 @@ struct B; fn main() { bla!(); //~^ ERROR cannot find macro `bla` - //~| HELP consider importing this macro } diff --git a/tests/ui/macros/issue-88228.stderr b/tests/ui/macros/issue-88228.stderr index fe8a1deaedd77..1dbe2b77be2d9 100644 --- a/tests/ui/macros/issue-88228.stderr +++ b/tests/ui/macros/issue-88228.stderr @@ -4,8 +4,10 @@ error: cannot find macro `bla` in this scope LL | bla!(); | ^^^ | - = help: consider importing this macro: - crate::hey::bla +help: consider importing this macro + | +LL + use crate::hey::bla; + | error: cannot find derive macro `println` in this scope --> $DIR/issue-88228.rs:14:10 @@ -16,13 +18,15 @@ LL | #[derive(println)] = note: `println` is in scope, but it is a function-like macro error: cannot find derive macro `Bla` in this scope - --> $DIR/issue-88228.rs:9:10 + --> $DIR/issue-88228.rs:10:10 | LL | #[derive(Bla)] | ^^^ | - = help: consider importing this derive macro: - crate::hey::Bla +help: consider importing this derive macro + | +LL + use crate::hey::Bla; + | error: aborting due to 3 previous errors diff --git a/tests/ui/macros/macro-use-wrong-name.stderr b/tests/ui/macros/macro-use-wrong-name.stderr index ca5f0f190e8ba..36339542ac61f 100644 --- a/tests/ui/macros/macro-use-wrong-name.stderr +++ b/tests/ui/macros/macro-use-wrong-name.stderr @@ -2,15 +2,21 @@ error: cannot find macro `macro_two` in this scope --> $DIR/macro-use-wrong-name.rs:7:5 | LL | macro_two!(); - | ^^^^^^^^^ help: a macro with a similar name exists: `macro_one` + | ^^^^^^^^^ | ::: $DIR/auxiliary/two_macros.rs:2:1 | LL | macro_rules! macro_one { () => ("one") } | ---------------------- similarly named macro `macro_one` defined here | - = help: consider importing this macro: - two_macros::macro_two +help: a macro with a similar name exists + | +LL | macro_one!(); + | ~~~~~~~~~ +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/missing/missing-macro-use.stderr b/tests/ui/missing/missing-macro-use.stderr index 99e291cda0377..e1d80f52daddc 100644 --- a/tests/ui/missing/missing-macro-use.stderr +++ b/tests/ui/missing/missing-macro-use.stderr @@ -4,8 +4,10 @@ error: cannot find macro `macro_two` in this scope LL | macro_two!(); | ^^^^^^^^^ | - = help: consider importing this macro: - two_macros::macro_two +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/proc-macro/derive-helper-shadowing.stderr b/tests/ui/proc-macro/derive-helper-shadowing.stderr index de2c27a878c67..7e7870b295127 100644 --- a/tests/ui/proc-macro/derive-helper-shadowing.stderr +++ b/tests/ui/proc-macro/derive-helper-shadowing.stderr @@ -16,9 +16,11 @@ error: cannot find attribute `empty_helper` in this scope LL | #[derive(GenHelperUse)] | ^^^^^^^^^^^^ | - = help: consider importing this attribute macro: - empty_helper = note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this attribute macro + | +LL + use empty_helper; + | error: cannot find attribute `empty_helper` in this scope --> $DIR/derive-helper-shadowing.rs:14:11 @@ -29,9 +31,11 @@ LL | #[empty_helper] LL | gen_helper_use!(); | ----------------- in this macro invocation | - = help: consider importing this attribute macro: - crate::empty_helper = note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this attribute macro + | +LL + use crate::empty_helper; + | error[E0659]: `empty_helper` is ambiguous --> $DIR/derive-helper-shadowing.rs:26:13 From a655b4d0162694193faa16f993cb0cad5ba45d48 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 9 Jun 2023 20:10:43 +0100 Subject: [PATCH 798/806] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index b0fa79679e717..49b6d9e179a91 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit b0fa79679e717cd077b7fc0fa4166f47107f1ba9 +Subproject commit 49b6d9e179a91cf7645142541c9563443f64bf2b From 46becfdf9c807e0108a78bb4df9b8330b10422c8 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 10 Apr 2023 14:20:38 +0300 Subject: [PATCH 799/806] expand: Change how `#![cfg(FALSE)]` behaves on crate root Previously it removed all other attributes from the crate root. Now it removes only attributes below itself. So it becomes possible to configure some global crate properties even for fully unconfigured crates. --- compiler/rustc_expand/src/config.rs | 8 +++++--- compiler/rustc_expand/src/expand.rs | 20 +++++++++++++++---- tests/ui/cfg/auxiliary/cfg_false_lib.rs | 6 ++---- .../auxiliary/cfg_false_lib_no_std_after.rs | 5 +++++ .../auxiliary/cfg_false_lib_no_std_before.rs | 8 ++++++++ tests/ui/cfg/cfg-false-feature.rs | 6 ++---- tests/ui/cfg/cfg-false-feature.stderr | 17 +++------------- tests/ui/cfg/cfg_false_no_std-1.rs | 10 ++++++++++ tests/ui/cfg/cfg_false_no_std-2.rs | 11 ++++++++++ tests/ui/cfg/cfg_false_no_std.rs | 3 +-- 10 files changed, 63 insertions(+), 31 deletions(-) create mode 100644 tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs create mode 100644 tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs create mode 100644 tests/ui/cfg/cfg_false_no_std-1.rs create mode 100644 tests/ui/cfg/cfg_false_no_std-2.rs diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 690f80f6876e4..bcfa5313bde3a 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -197,9 +197,11 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec config_tokens: false, lint_node_id: ast::CRATE_NODE_ID, }; - let attrs: ast::AttrVec = - attrs.iter().flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)).collect(); - if strip_unconfigured.in_cfg(&attrs) { attrs } else { ast::AttrVec::new() } + attrs + .iter() + .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) + .take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0) + .collect() } #[macro_export] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index dd8863df1953c..9850723a857e9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1039,7 +1039,12 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { ) -> Result { Ok(noop_flat_map(node, collector)) } - fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) { + fn expand_cfg_false( + &mut self, + collector: &mut InvocationCollector<'_, '_>, + _pos: usize, + span: Span, + ) { collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } @@ -1409,8 +1414,15 @@ impl InvocationCollectorNode for ast::Crate { fn noop_visit(&mut self, visitor: &mut V) { noop_visit_crate(self, visitor) } - fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, _span: Span) { - self.attrs.clear(); + fn expand_cfg_false( + &mut self, + collector: &mut InvocationCollector<'_, '_>, + pos: usize, + _span: Span, + ) { + // Attributes above `cfg(FALSE)` are left in place, because we may want to configure + // some global crate properties even on fully unconfigured crates. + self.attrs.truncate(pos); // Standard prelude imports are left in the crate for backward compatibility. self.items.truncate(collector.cx.num_standard_library_imports); } @@ -1804,7 +1816,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { continue; } - node.expand_cfg_false(self, span); + node.expand_cfg_false(self, pos, span); continue; } sym::cfg_attr => { diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib.rs b/tests/ui/cfg/auxiliary/cfg_false_lib.rs index 3c011d72b02c5..6c2dbb44d2a40 100644 --- a/tests/ui/cfg/auxiliary/cfg_false_lib.rs +++ b/tests/ui/cfg/auxiliary/cfg_false_lib.rs @@ -1,6 +1,4 @@ -// It is unclear whether a fully unconfigured crate should link to standard library, -// or what its `no_std`/`no_core`/`compiler_builtins` status, more precisely. -// Currently the usual standard library prelude is added to such crates, -// and therefore they link to libstd. +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// This crate has no such attribute, therefore this crate does link to libstd. #![cfg(FALSE)] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs new file mode 100644 index 0000000000000..3cfa6c510d020 --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs @@ -0,0 +1,5 @@ +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// Therefore this crate does link to libstd. + +#![cfg(FALSE)] +#![no_std] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs new file mode 100644 index 0000000000000..8e89545b8f40d --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs @@ -0,0 +1,8 @@ +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// Therefore this crate doesn't link to libstd. + +// no-prefer-dynamic + +#![no_std] +#![crate_type = "lib"] +#![cfg(FALSE)] diff --git a/tests/ui/cfg/cfg-false-feature.rs b/tests/ui/cfg/cfg-false-feature.rs index 21ea3ec79b4d6..84c231562f1e1 100644 --- a/tests/ui/cfg/cfg-false-feature.rs +++ b/tests/ui/cfg/cfg-false-feature.rs @@ -1,5 +1,4 @@ -// It is unclear which features should be in effect in a fully unconfigured crate (issue #104633). -// Currently none on the features are in effect, so we get the feature gates reported. +// Features above `cfg(FALSE)` are in effect in a fully unconfigured crate (issue #104633). // check-pass // compile-flags: --crate-type lib @@ -8,8 +7,7 @@ #![cfg(FALSE)] #![feature(box_syntax)] -macro mac() {} //~ WARN `macro` is experimental - //~| WARN unstable syntax can change at any point in the future +macro mac() {} // OK trait A = Clone; //~ WARN trait aliases are experimental //~| WARN unstable syntax can change at any point in the future diff --git a/tests/ui/cfg/cfg-false-feature.stderr b/tests/ui/cfg/cfg-false-feature.stderr index 14673fbdb1444..34093036205fe 100644 --- a/tests/ui/cfg/cfg-false-feature.stderr +++ b/tests/ui/cfg/cfg-false-feature.stderr @@ -1,5 +1,5 @@ warning: trait aliases are experimental - --> $DIR/cfg-false-feature.rs:14:1 + --> $DIR/cfg-false-feature.rs:12:1 | LL | trait A = Clone; | ^^^^^^^^^^^^^^^^ @@ -9,19 +9,8 @@ LL | trait A = Clone; = warning: unstable syntax can change at any point in the future, causing a hard error! = note: for more information, see issue #65860 -warning: `macro` is experimental - --> $DIR/cfg-false-feature.rs:11:1 - | -LL | macro mac() {} - | ^^^^^^^^^^^^^^ - | - = note: see issue #39412 for more information - = help: add `#![feature(decl_macro)]` to the crate attributes to enable - = warning: unstable syntax can change at any point in the future, causing a hard error! - = note: for more information, see issue #65860 - warning: box pattern syntax is experimental - --> $DIR/cfg-false-feature.rs:18:9 + --> $DIR/cfg-false-feature.rs:16:9 | LL | let box _ = Box::new(0); | ^^^^^ @@ -31,5 +20,5 @@ LL | let box _ = Box::new(0); = warning: unstable syntax can change at any point in the future, causing a hard error! = note: for more information, see issue #65860 -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/cfg/cfg_false_no_std-1.rs b/tests/ui/cfg/cfg_false_no_std-1.rs new file mode 100644 index 0000000000000..bcb49e5135364 --- /dev/null +++ b/tests/ui/cfg/cfg_false_no_std-1.rs @@ -0,0 +1,10 @@ +// No error, panic handler is supplied by libstd linked though the empty library. + +// check-pass +// aux-build: cfg_false_lib_no_std_after.rs + +#![no_std] + +extern crate cfg_false_lib_no_std_after as _; + +fn main() {} diff --git a/tests/ui/cfg/cfg_false_no_std-2.rs b/tests/ui/cfg/cfg_false_no_std-2.rs new file mode 100644 index 0000000000000..0a2bfd5f68b12 --- /dev/null +++ b/tests/ui/cfg/cfg_false_no_std-2.rs @@ -0,0 +1,11 @@ +// Error, the linked empty library is `no_std` and doesn't provide a panic handler. + +// dont-check-compiler-stderr +// error-pattern: `#[panic_handler]` function required, but not found +// aux-build: cfg_false_lib_no_std_before.rs + +#![no_std] + +extern crate cfg_false_lib_no_std_before as _; + +fn main() {} diff --git a/tests/ui/cfg/cfg_false_no_std.rs b/tests/ui/cfg/cfg_false_no_std.rs index 319ea078187c2..4fa831715ede1 100644 --- a/tests/ui/cfg/cfg_false_no_std.rs +++ b/tests/ui/cfg/cfg_false_no_std.rs @@ -1,5 +1,4 @@ -// Currently no error because the panic handler is supplied by libstd linked though the empty -// library, but the desirable behavior is unclear (see comments in cfg_false_lib.rs). +// No error, panic handler is supplied by libstd linked though the empty library. // check-pass // aux-build: cfg_false_lib.rs From 3152ac34bd36bd3b4e580f65f4c95b964fbfe34b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 9 Jun 2023 21:56:16 +0000 Subject: [PATCH 800/806] Ignore tests that hang in new solver --- .../ui/closures/issue-72408-nested-closures-exponential.rs | 1 + .../ui/issues/issue-37311-type-length-limit/issue-37311.rs | 1 + .../issues/issue-37311-type-length-limit/issue-37311.stderr | 4 ++-- tests/ui/iterators/issue-58952-filter-type-length.rs | 2 ++ tests/ui/recursion/issue-83150.rs | 1 + tests/ui/recursion/issue-83150.stderr | 6 +++--- tests/ui/traits/issue-91949-hangs-on-recursion.rs | 1 + tests/ui/traits/issue-91949-hangs-on-recursion.stderr | 4 ++-- 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/ui/closures/issue-72408-nested-closures-exponential.rs b/tests/ui/closures/issue-72408-nested-closures-exponential.rs index 2d6ba936572d5..d064ebceffd5c 100644 --- a/tests/ui/closures/issue-72408-nested-closures-exponential.rs +++ b/tests/ui/closures/issue-72408-nested-closures-exponential.rs @@ -1,4 +1,5 @@ // build-pass +// ignore-compare-mode-next-solver (hangs) // Closures include captured types twice in a type tree. // diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index 50d1f166c9865..c109be005238f 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,5 +1,6 @@ // build-fail // normalize-stderr-test: ".nll/" -> "/" +// ignore-compare-mode-next-solver (hangs) trait Mirror { type Image; diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 5b8299fe839d7..87832dd29b284 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,11 +1,11 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:17:9 + --> $DIR/issue-37311.rs:18:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:16:5 + --> $DIR/issue-37311.rs:17:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/iterators/issue-58952-filter-type-length.rs b/tests/ui/iterators/issue-58952-filter-type-length.rs index 6d12db8d13730..8e9cc84ec0339 100644 --- a/tests/ui/iterators/issue-58952-filter-type-length.rs +++ b/tests/ui/iterators/issue-58952-filter-type-length.rs @@ -1,4 +1,6 @@ // run-pass +// ignore-compare-mode-next-solver (hangs) + //! This snippet causes the type length to blowup exponentially, //! so check that we don't accidentally exceed the type length limit. // FIXME: Once the size of iterator adaptors is further reduced, diff --git a/tests/ui/recursion/issue-83150.rs b/tests/ui/recursion/issue-83150.rs index 38353d161c133..75dcdc59f21c1 100644 --- a/tests/ui/recursion/issue-83150.rs +++ b/tests/ui/recursion/issue-83150.rs @@ -2,6 +2,7 @@ // compile-flags: -Copt-level=0 // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" //~^^^ ERROR overflow evaluating the requirement +// ignore-compare-mode-next-solver (hangs) fn main() { let mut iter = 0u8..1; diff --git a/tests/ui/recursion/issue-83150.stderr b/tests/ui/recursion/issue-83150.stderr index 64683ae3a6ebd..eae58771a4169 100644 --- a/tests/ui/recursion/issue-83150.stderr +++ b/tests/ui/recursion/issue-83150.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-83150.rs:11:1 + --> $DIR/issue-83150.rs:12:1 | LL | fn func>(iter: &mut T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -9,10 +9,10 @@ LL | func(&mut iter.map(|x| x + 1)) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:12:24: 12:27]>: Iterator` +error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>: Iterator` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`) - = note: required for `&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:12:24: 12:27]>` to implement `Iterator` + = note: required for `&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>` to implement `Iterator` = note: 65 redundant requirements hidden = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>, ...>, ...>, ...>, ...>` to implement `Iterator` = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-83150/issue-83150.long-type-hash.txt' diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.rs b/tests/ui/traits/issue-91949-hangs-on-recursion.rs index 4eca643a92d4c..312d5d08c7df8 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.rs +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.rs @@ -3,6 +3,7 @@ // error-pattern: overflow evaluating the requirement ` as Iterator>::Item == ()` // error-pattern: function cannot return without recursing // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" +// ignore-compare-mode-next-solver (hangs) // Regression test for #91949. // This hanged *forever* on 1.56, fixed by #90423. diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr index 144990d50f000..c721dd41a2c15 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-91949-hangs-on-recursion.rs:23:1 + --> $DIR/issue-91949-hangs-on-recursion.rs:24:1 | LL | / fn recurse(elements: T) -> Vec LL | | where @@ -16,7 +16,7 @@ error[E0275]: overflow evaluating the requirement ` as Iter | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "512"]` attribute to your crate (`issue_91949_hangs_on_recursion`) note: required for `IteratorOfWrapped<(), std::iter::Empty<()>>` to implement `Iterator` - --> $DIR/issue-91949-hangs-on-recursion.rs:16:32 + --> $DIR/issue-91949-hangs-on-recursion.rs:17:32 | LL | impl> Iterator for IteratorOfWrapped { | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ From cec95d74dd61cbe4dfef677f6b2e6436ba216fce Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 9 Jun 2023 23:55:26 -0400 Subject: [PATCH 801/806] Add deprecation warning for python <3.6 in x.py Soft deprecate old python versions to give users a warning that eventually it may not be supported. --- x.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/x.py b/x.py index b8cdf67712c43..7163df5cfe99b 100755 --- a/x.py +++ b/x.py @@ -9,12 +9,17 @@ if __name__ == '__main__': import os import sys + import warnings + from inspect import cleandoc + + major = sys.version_info.major + minor = sys.version_info.minor # If this is python2, check if python3 is available and re-execute with that # interpreter. Only python3 allows downloading CI LLVM. # # This matters if someone's system `python` is python2. - if sys.version_info.major < 3: + if major < 3: try: os.execvp("py", ["py", "-3"] + sys.argv) except OSError: @@ -24,6 +29,19 @@ # Python 3 isn't available, fall back to python 2 pass + # soft deprecation of old python versions + skip_check = os.environ.get("RUST_IGNORE_OLD_PYTHON") == "1" + if major < 3 or (major == 3 and minor < 6): + msg = cleandoc(""" + Using python {}.{} but >= 3.6 is recommended. Your python version + should continue to work for the near future, but this will + eventually change. If python >= 3.6 is not available on your system, + please file an issue to help us understand timelines. + + This message can be suppressed by setting `RUST_IGNORE_OLD_PYTHON=1` + """.format(major, minor)) + warnings.warn(msg) + rust_dir = os.path.dirname(os.path.abspath(__file__)) # For the import below, have Python search in src/bootstrap first. sys.path.insert(0, os.path.join(rust_dir, "src", "bootstrap")) From 80176a120e90bb9f8e7b9e8fedda9ac857f434f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Thu, 8 Jun 2023 15:03:21 +0800 Subject: [PATCH 802/806] Adjust span labels for `HIDDEN_GLOB_REEXPORTS` --- compiler/rustc_lint/src/context.rs | 4 +- tests/ui/resolve/hidden_glob_reexports.rs | 4 +- tests/ui/resolve/hidden_glob_reexports.stderr | 45 ++++++++++++++----- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 947530a1b65a9..13164b0b339a3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -953,8 +953,8 @@ pub trait LintContext: Sized { db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace)); } BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => { - db.span_label(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace)); - db.span_label(private_item_span, "but the private item here shadows it"); + db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace)); + db.span_note(private_item_span, "but the private item here shadows it".to_owned()); } } // Rewrap `db`, and pass control to the user. diff --git a/tests/ui/resolve/hidden_glob_reexports.rs b/tests/ui/resolve/hidden_glob_reexports.rs index 361243fcd7bdf..102b565624526 100644 --- a/tests/ui/resolve/hidden_glob_reexports.rs +++ b/tests/ui/resolve/hidden_glob_reexports.rs @@ -6,10 +6,10 @@ pub mod upstream_a { pub struct Bar {} } - pub use self::inner::*; - struct Foo; //~^ WARN private item shadows public glob re-export + + pub use self::inner::*; } pub mod upstream_b { diff --git a/tests/ui/resolve/hidden_glob_reexports.stderr b/tests/ui/resolve/hidden_glob_reexports.stderr index ddf7bcda827a8..11fa94d6fee98 100644 --- a/tests/ui/resolve/hidden_glob_reexports.stderr +++ b/tests/ui/resolve/hidden_glob_reexports.stderr @@ -1,31 +1,54 @@ warning: private item shadows public glob re-export - --> $DIR/hidden_glob_reexports.rs:11:5 + --> $DIR/hidden_glob_reexports.rs:9:5 | -LL | pub use self::inner::*; - | -------------- the name `Foo` in the type namespace is supposed to be publicly re-exported here -LL | LL | struct Foo; - | ^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^ + | +note: the name `Foo` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:12:13 + | +LL | pub use self::inner::*; + | ^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:9:5 | +LL | struct Foo; + | ^^^^^^^^^^^ = note: `#[warn(hidden_glob_reexports)]` on by default warning: private item shadows public glob re-export --> $DIR/hidden_glob_reexports.rs:27:9 | +LL | use self::other::Foo; + | ^^^^^^^^^^^^^^^^ + | +note: the name `Foo` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:25:13 + | LL | pub use self::inner::*; - | -------------- the name `Foo` in the type namespace is supposed to be publicly re-exported here -LL | + | ^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:27:9 + | LL | use self::other::Foo; - | ^^^^^^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^^^^^^ warning: private item shadows public glob re-export --> $DIR/hidden_glob_reexports.rs:40:9 | +LL | use std::primitive::u8; + | ^^^^^^^^^^^^^^^^^^ + | +note: the name `u8` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:38:13 + | LL | pub use self::no_def_id::*; - | ------------------ the name `u8` in the type namespace is supposed to be publicly re-exported here -LL | + | ^^^^^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:40:9 + | LL | use std::primitive::u8; - | ^^^^^^^^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^^^^^^^^ warning: 3 warnings emitted From 3e91349c427976a211a55f656771a427f4251fa7 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 20 May 2023 23:05:09 +0200 Subject: [PATCH 803/806] Uplift improved version of `clippy::cmp_nan` to rustc --- compiler/rustc_lint/messages.ftl | 5 + compiler/rustc_lint/src/lints.rs | 30 ++++ compiler/rustc_lint/src/types.rs | 102 ++++++++++- tests/ui/issues/issue-50811.rs | 1 + .../invalid-nan-comparison-suggestion.fixed | 36 ++++ .../lint/invalid-nan-comparison-suggestion.rs | 39 +++++ .../invalid-nan-comparison-suggestion.stderr | 114 +++++++++++++ tests/ui/lint/invalid-nan-comparison.rs | 51 ++++++ tests/ui/lint/invalid-nan-comparison.stderr | 159 ++++++++++++++++++ 9 files changed, 530 insertions(+), 7 deletions(-) create mode 100644 tests/ui/lint/invalid-nan-comparison-suggestion.fixed create mode 100644 tests/ui/lint/invalid-nan-comparison-suggestion.rs create mode 100644 tests/ui/lint/invalid-nan-comparison-suggestion.stderr create mode 100644 tests/ui/lint/invalid-nan-comparison.rs create mode 100644 tests/ui/lint/invalid-nan-comparison.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 51eb8210d46d5..5e13ee7b8a41d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -314,6 +314,11 @@ lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal alw lint_invalid_from_utf8_unchecked = calls to `{$method}` with a invalid literal are undefined behavior .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes +lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be directly compared to itself + .suggestion = use `f32::is_nan()` or `f64::is_nan()` instead + +lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable + lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 03dbffcc2c435..e990c771bdf50 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1434,6 +1434,36 @@ pub struct OverflowingLiteral<'a> { #[diag(lint_unused_comparisons)] pub struct UnusedComparisons; +#[derive(LintDiagnostic)] +pub enum InvalidNanComparisons { + #[diag(lint_invalid_nan_comparisons_eq_ne)] + EqNe { + #[subdiagnostic] + suggestion: InvalidNanComparisonsSuggestion, + }, + #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)] + LtLeGtGe, +} + +#[derive(Subdiagnostic)] +pub enum InvalidNanComparisonsSuggestion { + #[multipart_suggestion( + lint_suggestion, + style = "verbose", + applicability = "machine-applicable" + )] + Spanful { + #[suggestion_part(code = "!")] + neg: Option, + #[suggestion_part(code = ".is_nan()")] + float: Span, + #[suggestion_part(code = "")] + nan_plus_binop: Span, + }, + #[help(lint_suggestion)] + Spanless, +} + pub struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 4bf4fda8292b6..264a59c5585a0 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -2,10 +2,10 @@ use crate::{ fluent_generated as fluent, lints::{ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, - InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, - OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, - OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, - VariantSizeDifferencesDiag, + InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, + OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSub, + OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, + RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag, }, }; use crate::{LateContext, LateLintPass, LintContext}; @@ -113,13 +113,35 @@ declare_lint! { "detects enums with widely varying variant sizes" } +declare_lint! { + /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN` + /// as one of the operand. + /// + /// ### Example + /// + /// ```rust + /// let a = 2.3f32; + /// if a == f32::NAN {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// NaN does not compare meaningfully to anything – not + /// even itself – so those comparisons are always false. + INVALID_NAN_COMPARISONS, + Warn, + "detects invalid floating point NaN comparisons" +} + #[derive(Copy, Clone)] pub struct TypeLimits { /// Id of the last visited negated expression negated_expr_id: Option, } -impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]); +impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]); impl TypeLimits { pub fn new() -> TypeLimits { @@ -486,6 +508,68 @@ fn lint_literal<'tcx>( } } +fn lint_nan<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + binop: hir::BinOp, + l: &'tcx hir::Expr<'tcx>, + r: &'tcx hir::Expr<'tcx>, +) { + fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + let expr = expr.peel_blocks().peel_borrows(); + match expr.kind { + ExprKind::Path(qpath) => { + let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; }; + + matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan)) + } + _ => false, + } + } + + fn eq_ne( + e: &hir::Expr<'_>, + l: &hir::Expr<'_>, + r: &hir::Expr<'_>, + f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion, + ) -> InvalidNanComparisons { + let suggestion = + if let Some(l_span) = l.span.find_ancestor_inside(e.span) && + let Some(r_span) = r.span.find_ancestor_inside(e.span) { + f(l_span, r_span) + } else { + InvalidNanComparisonsSuggestion::Spanless + }; + + InvalidNanComparisons::EqNe { suggestion } + } + + let lint = match binop.node { + hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { + eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + nan_plus_binop: l_span.until(r_span), + float: r_span.shrink_to_hi(), + neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), + }) + } + hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { + eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + nan_plus_binop: l_span.shrink_to_hi().to(r_span), + float: l_span.shrink_to_hi(), + neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), + }) + } + hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge + if is_nan(cx, l) || is_nan(cx, r) => + { + InvalidNanComparisons::LtLeGtGe + } + _ => return, + }; + + cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint); +} + impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { @@ -496,8 +580,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } hir::ExprKind::Binary(binop, ref l, ref r) => { - if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { - cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); + if is_comparison(binop) { + if !check_limits(cx, binop, &l, &r) { + cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); + } else { + lint_nan(cx, e, binop, l, r); + } } } hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), diff --git a/tests/ui/issues/issue-50811.rs b/tests/ui/issues/issue-50811.rs index 683c856049fdc..2a20e50fa4543 100644 --- a/tests/ui/issues/issue-50811.rs +++ b/tests/ui/issues/issue-50811.rs @@ -1,5 +1,6 @@ // run-pass #![feature(test)] +#![allow(invalid_nan_comparisons)] extern crate test; diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.fixed b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed new file mode 100644 index 0000000000000..feafc6c1b8c13 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed @@ -0,0 +1,36 @@ +// check-pass +// run-rustfix + +fn main() { + let x = 5f32; + let _ = x.is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !x.is_nan(); + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = x.is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !x.is_nan(); + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !b.is_nan() {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !b.is_nan() {} + //~^ WARN incorrect NaN comparison + + let _ = + !b.is_nan(); + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = number!().is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !number!().is_nan(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.rs b/tests/ui/lint/invalid-nan-comparison-suggestion.rs new file mode 100644 index 0000000000000..ad5eb66e5f17d --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.rs @@ -0,0 +1,39 @@ +// check-pass +// run-rustfix + +fn main() { + let x = 5f32; + let _ = x == f32::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f32::NAN; + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = x == f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f64::NAN; + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != &f32::NAN {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != { &f32::NAN } {} + //~^ WARN incorrect NaN comparison + + let _ = + b != { + //~^ WARN incorrect NaN comparison + &f32::NAN + }; + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = nan!() == number!(); + //~^ WARN incorrect NaN comparison + let _ = number!() != nan!(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.stderr b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr new file mode 100644 index 0000000000000..c310341de07b7 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr @@ -0,0 +1,114 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:6:13 + | +LL | let _ = x == f32::NAN; + | ^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x == f32::NAN; +LL + let _ = x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:8:13 + | +LL | let _ = x != f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x != f32::NAN; +LL + let _ = !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:12:13 + | +LL | let _ = x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x == f64::NAN; +LL + let _ = x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:14:13 + | +LL | let _ = x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x != f64::NAN; +LL + let _ = !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:18:8 + | +LL | if b != &f32::NAN {} + | ^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - if b != &f32::NAN {} +LL + if !b.is_nan() {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:22:8 + | +LL | if b != { &f32::NAN } {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - if b != { &f32::NAN } {} +LL + if !b.is_nan() {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:26:9 + | +LL | / b != { +LL | | +LL | | &f32::NAN +LL | | }; + | |_________^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - b != { +LL + !b.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:35:13 + | +LL | let _ = nan!() == number!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = nan!() == number!(); +LL + let _ = number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:37:13 + | +LL | let _ = number!() != nan!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = number!() != nan!(); +LL + let _ = !number!().is_nan(); + | + +warning: 9 warnings emitted + diff --git a/tests/ui/lint/invalid-nan-comparison.rs b/tests/ui/lint/invalid-nan-comparison.rs new file mode 100644 index 0000000000000..d7e793ca58301 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison.rs @@ -0,0 +1,51 @@ +// check-pass + +fn main() { + f32(); + f64(); +} + +const TEST: bool = 5f32 == f32::NAN; +//~^ WARN incorrect NaN comparison + +fn f32() { + macro_rules! number { () => { 5f32 }; } + let x = number!(); + x == f32::NAN; + //~^ WARN incorrect NaN comparison + x != f32::NAN; + //~^ WARN incorrect NaN comparison + x < f32::NAN; + //~^ WARN incorrect NaN comparison + x > f32::NAN; + //~^ WARN incorrect NaN comparison + x <= f32::NAN; + //~^ WARN incorrect NaN comparison + x >= f32::NAN; + //~^ WARN incorrect NaN comparison + number!() == f32::NAN; + //~^ WARN incorrect NaN comparison + f32::NAN != number!(); + //~^ WARN incorrect NaN comparison +} + +fn f64() { + macro_rules! number { () => { 5f64 }; } + let x = number!(); + x == f64::NAN; + //~^ WARN incorrect NaN comparison + x != f64::NAN; + //~^ WARN incorrect NaN comparison + x < f64::NAN; + //~^ WARN incorrect NaN comparison + x > f64::NAN; + //~^ WARN incorrect NaN comparison + x <= f64::NAN; + //~^ WARN incorrect NaN comparison + x >= f64::NAN; + //~^ WARN incorrect NaN comparison + number!() == f64::NAN; + //~^ WARN incorrect NaN comparison + f64::NAN != number!(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison.stderr b/tests/ui/lint/invalid-nan-comparison.stderr new file mode 100644 index 0000000000000..054c06d38b30b --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison.stderr @@ -0,0 +1,159 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:8:20 + | +LL | const TEST: bool = 5f32 == f32::NAN; + | ^^^^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - const TEST: bool = 5f32 == f32::NAN; +LL + const TEST: bool = 5f32.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:14:5 + | +LL | x == f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x == f32::NAN; +LL + x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:16:5 + | +LL | x != f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x != f32::NAN; +LL + !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:18:5 + | +LL | x < f32::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:20:5 + | +LL | x > f32::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:22:5 + | +LL | x <= f32::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:24:5 + | +LL | x >= f32::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:26:5 + | +LL | number!() == f32::NAN; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - number!() == f32::NAN; +LL + number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:28:5 + | +LL | f32::NAN != number!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - f32::NAN != number!(); +LL + !number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:35:5 + | +LL | x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x == f64::NAN; +LL + x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:37:5 + | +LL | x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x != f64::NAN; +LL + !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:39:5 + | +LL | x < f64::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:41:5 + | +LL | x > f64::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:43:5 + | +LL | x <= f64::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:45:5 + | +LL | x >= f64::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:47:5 + | +LL | number!() == f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - number!() == f64::NAN; +LL + number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:49:5 + | +LL | f64::NAN != number!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - f64::NAN != number!(); +LL + !number!().is_nan(); + | + +warning: 17 warnings emitted + From a8145372d70f0aeee27546219bdc9564420cc974 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 21 May 2023 12:12:12 +0200 Subject: [PATCH 804/806] Drop uplifted `clippy:cmp_nan` --- .../clippy/clippy_lints/src/declared_lints.rs | 1 - .../clippy_lints/src/operators/cmp_nan.rs | 30 ---- .../clippy/clippy_lints/src/operators/mod.rs | 28 ---- .../clippy/clippy_lints/src/renamed_lints.rs | 1 + src/tools/clippy/tests/ui/cmp_nan.rs | 34 ---- src/tools/clippy/tests/ui/cmp_nan.stderr | 148 ------------------ .../clippy/tests/ui/crashes/mut_mut_macro.rs | 5 +- src/tools/clippy/tests/ui/rename.fixed | 2 + src/tools/clippy/tests/ui/rename.rs | 2 + src/tools/clippy/tests/ui/rename.stderr | 110 +++++++------ .../tests/ui/unknown_clippy_lints.fixed | 2 +- .../clippy/tests/ui/unknown_clippy_lints.rs | 2 +- .../tests/ui/unknown_clippy_lints.stderr | 6 +- 13 files changed, 70 insertions(+), 301 deletions(-) delete mode 100644 src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs delete mode 100644 src/tools/clippy/tests/ui/cmp_nan.rs delete mode 100644 src/tools/clippy/tests/ui/cmp_nan.stderr diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 6270d26131323..f1d1355123ab6 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -476,7 +476,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, crate::operators::BAD_BIT_MASK_INFO, - crate::operators::CMP_NAN_INFO, crate::operators::CMP_OWNED_INFO, crate::operators::DOUBLE_COMPARISONS_INFO, crate::operators::DURATION_SUBSEC_INFO, diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs deleted file mode 100644 index e18064b7061bf..0000000000000 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs +++ /dev/null @@ -1,30 +0,0 @@ -use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::in_constant; -use rustc_hir::{BinOpKind, Expr}; -use rustc_lint::LateContext; - -use super::CMP_NAN; - -pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { - if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) { - span_lint( - cx, - CMP_NAN, - e.span, - "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", - ); - } -} - -fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - if let Some(value) = constant(cx, cx.typeck_results(), e) { - match value { - Constant::F32(num) => num.is_nan(), - Constant::F64(num) => num.is_nan(), - _ => false, - } - } else { - false - } -} diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index d63a836e73d6f..2cf15adda01a2 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -1,7 +1,6 @@ mod absurd_extreme_comparisons; mod assign_op_pattern; mod bit_mask; -mod cmp_nan; mod cmp_owned; mod double_comparison; mod duration_subsec; @@ -485,31 +484,6 @@ declare_clippy_lint! { "integer division may cause loss of precision" } -declare_clippy_lint! { - /// ### What it does - /// Checks for comparisons to NaN. - /// - /// ### Why is this bad? - /// NaN does not compare meaningfully to anything – not - /// even itself – so those comparisons are simply wrong. - /// - /// ### Example - /// ```rust - /// # let x = 1.0; - /// if x == f32::NAN { } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let x = 1.0f32; - /// if x.is_nan() { } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub CMP_NAN, - correctness, - "comparisons to `NAN`, which will always return false, probably not intended" -} - declare_clippy_lint! { /// ### What it does /// Checks for conversions to owned values just for the sake @@ -775,7 +749,6 @@ impl_lint_pass!(Operators => [ FLOAT_EQUALITY_WITHOUT_ABS, IDENTITY_OP, INTEGER_DIVISION, - CMP_NAN, CMP_OWNED, FLOAT_CMP, FLOAT_CMP_CONST, @@ -816,7 +789,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { duration_subsec::check(cx, e, op.node, lhs, rhs); float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); - cmp_nan::check(cx, e, op.node, lhs, rhs); cmp_owned::check(cx, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index f76fa76f0764e..cbcd11debfd76 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -33,6 +33,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::cast_ref_to_mut", "cast_ref_to_mut"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), + ("clippy::cmp_nan", "invalid_nan_comparisons"), ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "dropping_references"), diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs deleted file mode 100644 index 64ca52b010a7e..0000000000000 --- a/src/tools/clippy/tests/ui/cmp_nan.rs +++ /dev/null @@ -1,34 +0,0 @@ -const NAN_F32: f32 = f32::NAN; -const NAN_F64: f64 = f64::NAN; - -#[warn(clippy::cmp_nan)] -#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)] -fn main() { - let x = 5f32; - x == f32::NAN; - x != f32::NAN; - x < f32::NAN; - x > f32::NAN; - x <= f32::NAN; - x >= f32::NAN; - x == NAN_F32; - x != NAN_F32; - x < NAN_F32; - x > NAN_F32; - x <= NAN_F32; - x >= NAN_F32; - - let y = 0f64; - y == f64::NAN; - y != f64::NAN; - y < f64::NAN; - y > f64::NAN; - y <= f64::NAN; - y >= f64::NAN; - y == NAN_F64; - y != NAN_F64; - y < NAN_F64; - y > NAN_F64; - y <= NAN_F64; - y >= NAN_F64; -} diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr deleted file mode 100644 index 867516661a539..0000000000000 --- a/src/tools/clippy/tests/ui/cmp_nan.stderr +++ /dev/null @@ -1,148 +0,0 @@ -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:8:5 - | -LL | x == f32::NAN; - | ^^^^^^^^^^^^^ - | - = note: `-D clippy::cmp-nan` implied by `-D warnings` - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:9:5 - | -LL | x != f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:10:5 - | -LL | x < f32::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:11:5 - | -LL | x > f32::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:12:5 - | -LL | x <= f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:13:5 - | -LL | x >= f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:14:5 - | -LL | x == NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:15:5 - | -LL | x != NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:16:5 - | -LL | x < NAN_F32; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:17:5 - | -LL | x > NAN_F32; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:18:5 - | -LL | x <= NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:19:5 - | -LL | x >= NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:22:5 - | -LL | y == f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:23:5 - | -LL | y != f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:24:5 - | -LL | y < f64::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:25:5 - | -LL | y > f64::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:26:5 - | -LL | y <= f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:27:5 - | -LL | y >= f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:28:5 - | -LL | y == NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:29:5 - | -LL | y != NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:30:5 - | -LL | y < NAN_F64; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:31:5 - | -LL | y > NAN_F64; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:32:5 - | -LL | y <= NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:33:5 - | -LL | y >= NAN_F64; - | ^^^^^^^^^^^^ - -error: aborting due to 24 previous errors - diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs index a238e7896fc6b..92821b6ecbbab 100644 --- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs +++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs @@ -1,4 +1,4 @@ -#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] +#![deny(clippy::mut_mut, clippy::zero_ptr)] #![allow(dead_code)] // FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need @@ -8,13 +8,12 @@ // extern crate lazy_static; // use std::collections::HashMap; -/// ensure that we don't suggest `is_nan` and `is_null` inside constants +/// ensure that we don't suggest `is_null` inside constants /// FIXME: once const fn is stable, suggest these functions again in constants const BAA: *const i32 = 0 as *const i32; static mut BAR: *const i32 = BAA; static mut FOO: *const i32 = 0 as *const i32; -static mut BUH: bool = 42.0 < f32::NAN; #[allow(unused_variables, unused_mut)] fn main() { diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 954145fc4e64b..f1b25dc094ef6 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -41,6 +41,7 @@ #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] +#![allow(invalid_nan_comparisons)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] @@ -55,6 +56,7 @@ #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::box_collection)] +#![warn(invalid_nan_comparisons)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] #![warn(clippy::derived_hash_with_manual_eq)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 067a3109afb97..4af511c344c4d 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -41,6 +41,7 @@ #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] +#![allow(invalid_nan_comparisons)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] @@ -55,6 +56,7 @@ #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] #![warn(clippy::box_vec)] +#![warn(clippy::cmp_nan)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] #![warn(clippy::derive_hash_xor_eq)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 1819d108c5740..156a8f96b5026 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,304 +7,310 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` + --> $DIR/rename.rs:59:9 + | +LL | #![warn(clippy::cmp_nan)] + | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` + error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:102:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 51 previous errors +error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed index 49c0e4dc7eb10..debc7e152e739 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -3,7 +3,7 @@ #![warn(clippy::pedantic)] // Should suggest lowercase #![allow(clippy::all)] -#![warn(clippy::cmp_nan)] +#![warn(clippy::cmp_owned)] // Should suggest similar clippy lint name #[warn(clippy::if_not_else)] diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs index b60042923ea18..16140fd107917 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -3,7 +3,7 @@ #![warn(clippy::pedantic)] // Should suggest lowercase #![allow(clippy::All)] -#![warn(clippy::CMP_NAN)] +#![warn(clippy::CMP_OWNED)] // Should suggest similar clippy lint name #[warn(clippy::if_not_els)] diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr index 584c428932fee..880673eef3e40 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -6,11 +6,11 @@ LL | #![allow(clippy::All)] | = note: `-D unknown-lints` implied by `-D warnings` -error: unknown lint: `clippy::CMP_NAN` +error: unknown lint: `clippy::CMP_OWNED` --> $DIR/unknown_clippy_lints.rs:6:9 | -LL | #![warn(clippy::CMP_NAN)] - | ^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_nan` +LL | #![warn(clippy::CMP_OWNED)] + | ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned` error: unknown lint: `clippy::if_not_els` --> $DIR/unknown_clippy_lints.rs:9:8 From 0220c0b765e92dcb3565f0a8bd3c422afbd77f88 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 11 Jun 2023 14:36:20 +0800 Subject: [PATCH 805/806] Detect actual span for getting unexpected token from parsing macros --- compiler/rustc_parse/src/parser/expr.rs | 10 ++++++++-- tests/ui/parser/issues/issue-112458.rs | 4 ++++ tests/ui/parser/issues/issue-112458.stderr | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/ui/parser/issues/issue-112458.rs create mode 100644 tests/ui/parser/issues/issue-112458.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cea2a71c98821..d81b389920148 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1013,9 +1013,15 @@ impl<'a> Parser<'a> { } fn error_unexpected_after_dot(&self) { - // FIXME Could factor this out into non_fatal_unexpected or something. let actual = pprust::token_to_string(&self.token); - self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual }); + let span = self.token.span; + let sm = self.sess.source_map(); + let (span, actual) = match (&self.token.kind, self.subparser_name) { + (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => + (span.shrink_to_hi(), actual.into()), + _ => (span, actual), + }; + self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } // We need an identifier or integer, but the next token is a float. diff --git a/tests/ui/parser/issues/issue-112458.rs b/tests/ui/parser/issues/issue-112458.rs new file mode 100644 index 0000000000000..36895450cd94f --- /dev/null +++ b/tests/ui/parser/issues/issue-112458.rs @@ -0,0 +1,4 @@ +fn main() { + println!("{}", x.); //~ ERROR unexpected token: `)` + //~^ ERROR cannot find value `x` in this scope +} diff --git a/tests/ui/parser/issues/issue-112458.stderr b/tests/ui/parser/issues/issue-112458.stderr new file mode 100644 index 0000000000000..54a8f1d03b02a --- /dev/null +++ b/tests/ui/parser/issues/issue-112458.stderr @@ -0,0 +1,15 @@ +error: unexpected token: `)` + --> $DIR/issue-112458.rs:2:22 + | +LL | println!("{}", x.); + | ^ + +error[E0425]: cannot find value `x` in this scope + --> $DIR/issue-112458.rs:2:20 + | +LL | println!("{}", x.); + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. From 6147833064af1391978f0e24bda1993ef907e647 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 Jun 2023 22:08:00 +0200 Subject: [PATCH 806/806] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ee815ae6f8fc0..918c738ed1fe5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -33c3d101280c8eb3cd8af421bfb56a8afcc3881d +37998ab508d5d9fa0d465d7b535dc673087dda8f

', - 'a<"P">', - '"P" "P"', - 'P "P"', - '"p" p', - '"const": p', - "a<:a>", - "a<::a>", - "((a))", - "(p -> p", - "::a::b", - "a::::b", - "a::b::", - ":a", - "a b:", - "a (b:", - "_:", - "_:a", - "a-bb", - "a>bb", - "ab'", - "a->", - '"p" ', - '"p" a', - "a,<", - "aaaaa<>b", - "fn:aaaaa<>b", - "->a<>b", - "a<->", - "a:: a", - "a ::a", - "a:", - "a<>:", - "a,:", - " a<> :", - "mod : :", - "a!a", - "a!!", - "mod:a!", - "a!::a", - "a<", -]; - const PARSED = [ { + query: '