From e45a250f8c26e94a2a3ff8b50305530f75718239 Mon Sep 17 00:00:00 2001 From: Zachary S <zasample18+github@gmail.com> Date: Mon, 25 Jul 2022 15:43:25 -0500 Subject: [PATCH 01/28] fix: Insert spaces when inlining a function defined in a macro. --- .../ide-assists/src/handlers/inline_call.rs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 658a1aadf53ec..8b2f6ac75a28e 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use ide_db::{ imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, search::{FileReference, SearchScope}, - syntax_helpers::node_ext::expr_as_name_ref, + syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, RootDatabase, }; use itertools::{izip, Itertools}; @@ -301,7 +301,18 @@ fn inline( params: &[(ast::Pat, Option<ast::Type>, hir::Param)], CallInfo { node, arguments, generic_arg_list }: &CallInfo, ) -> ast::Expr { - let body = fn_body.clone_for_update(); + let body = if sema.hir_file_for(fn_body.syntax()).is_macro() { + cov_mark::hit!(inline_call_defined_in_macro); + if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { + body + } else { + // FIXME(zachs18): I believe this should be unreachable, + // since insert_ws_into shouldn't change the kind of the SyntaxNode. + fn_body.clone_for_update() + } + } else { + fn_body.clone_for_update() + }; let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -1144,6 +1155,41 @@ fn bar() -> u32 { x }) + foo() } +"#, + ) + } + + #[test] + fn inline_call_defined_in_macro() { + cov_mark::check!(inline_call_defined_in_macro); + check_assist( + inline_call, + r#" +macro_rules! define_foo { + () => { fn foo() -> u32 { + let x = 0; + x + } }; +} +define_foo!(); +fn bar() -> u32 { + foo$0() +} +"#, + r#" +macro_rules! define_foo { + () => { fn foo() -> u32 { + let x = 0; + x + } }; +} +define_foo!(); +fn bar() -> u32 { + { + let x = 0; + x + } +} "#, ) } From b1e3daf14b14404945a14346d7b478feee7c5508 Mon Sep 17 00:00:00 2001 From: Amos Wenger <amoswenger@gmail.com> Date: Tue, 26 Jul 2022 11:30:41 +0200 Subject: [PATCH 02/28] Find standalone proc-macro-srv on windows too --- crates/rust-analyzer/src/reload.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 9ae361b034e28..b9aa13ec5b33b 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -303,6 +303,9 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); + let standalone_server_name = + format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); + if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { self.proc_macro_clients = self @@ -316,10 +319,8 @@ impl GlobalState { tracing::info!("Found a cargo workspace..."); if let Some(sysroot) = sysroot.as_ref() { tracing::info!("Found a cargo workspace with a sysroot..."); - let server_path = sysroot - .root() - .join("libexec") - .join("rust-analyzer-proc-macro-srv"); + let server_path = + sysroot.root().join("libexec").join(&standalone_server_name); if std::fs::metadata(&server_path).is_ok() { tracing::info!( "And the server exists at {}", From c8ff70e924efe3adba39ac5dc2a93dbd94bde5a4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Tue, 26 Jul 2022 16:30:45 +0200 Subject: [PATCH 03/28] fix: Fix server panicking on project loading when proc-macros are disabled --- crates/rust-analyzer/src/cli/load_cargo.rs | 4 +++- crates/rust-analyzer/src/reload.rs | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 0ada4b73e842d..5d1c013c3275b 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -66,7 +66,9 @@ pub fn load_workspace( }; let crate_graph = ws.to_crate_graph( - &mut |_, path: &AbsPath| load_proc_macro(proc_macro_client.as_ref(), path, &[]), + &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()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index b9aa13ec5b33b..eaab275bc68a4 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -390,7 +390,10 @@ impl GlobalState { let mut crate_graph = CrateGraph::default(); for (idx, ws) in self.workspaces.iter().enumerate() { - let proc_macro_client = self.proc_macro_clients[idx].as_ref(); + 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, @@ -574,7 +577,7 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` /// with an identity dummy expander. pub(crate) fn load_proc_macro( - server: Result<&ProcMacroServer, &String>, + server: Result<&ProcMacroServer, &str>, path: &AbsPath, dummy_replace: &[Box<str>], ) -> ProcMacroLoadResult { From add33b65dda4b59b46b5192f3a88a83b771af844 Mon Sep 17 00:00:00 2001 From: Zachary S <zasample18+github@gmail.com> Date: Tue, 26 Jul 2022 10:59:19 -0500 Subject: [PATCH 04/28] Remove FIXME comment for unreachable fallback. --- crates/ide-assists/src/handlers/inline_call.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 8b2f6ac75a28e..80d3b92559367 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -306,8 +306,6 @@ fn inline( if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { body } else { - // FIXME(zachs18): I believe this should be unreachable, - // since insert_ws_into shouldn't change the kind of the SyntaxNode. fn_body.clone_for_update() } } else { From 6c379b9f4bb9774758e018944a33db5eb29622fb Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 27 Jul 2022 12:45:29 +0200 Subject: [PATCH 05/28] fix: Fix Semantics::original_ast_node not caching the resulting file --- crates/hir/src/semantics.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index fc8f23f19ab91..a75e5cafd0bd1 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -924,7 +924,12 @@ impl<'db> SemanticsImpl<'db> { } fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> { - self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(|it| it.value) + self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map( + |InFile { file_id, value }| { + self.cache(find_root(value.syntax()), file_id); + value + }, + ) } fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange { From 1f8daa180fdfb847f5f82b8ac9870aebb17c9494 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 27 Jul 2022 13:48:26 +0200 Subject: [PATCH 06/28] fix: Honor ref expressions for compute_ref_match completions --- crates/hir-ty/src/chalk_ext.rs | 5 ++ crates/hir/src/lib.rs | 4 ++ crates/ide-completion/src/context/analysis.rs | 69 ++++++++++++------- crates/ide-completion/src/context/tests.rs | 20 ++++++ 4 files changed, 75 insertions(+), 23 deletions(-) diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index b0885ab003f71..a9c124b42dc2c 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -34,6 +34,7 @@ pub trait TyExt { fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>; fn strip_references(&self) -> &Ty; + fn strip_reference(&self) -> &Ty; /// If this is a `dyn Trait`, returns that trait. fn dyn_trait(&self) -> Option<TraitId>; @@ -182,6 +183,10 @@ impl TyExt for Ty { t } + fn strip_reference(&self) -> &Ty { + self.as_reference().map_or(self, |(ty, _, _)| ty) + } + fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> { match self.kind(Interner) { TyKind::OpaqueType(opaque_ty_id, subst) => { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d4925455d7bd2..8f984210e1176 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2769,6 +2769,10 @@ impl Type { self.derived(self.ty.strip_references().clone()) } + pub fn strip_reference(&self) -> Type { + self.derived(self.ty.strip_reference().clone()) + } + pub fn is_unknown(&self) -> bool { self.ty.is_unknown() } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index c71ffa0ed86fd..09a1a99eb64ca 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -162,11 +162,52 @@ impl<'a> CompletionContext<'a> { } /// Calculate the expected type and name of the cursor position. - fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) { + fn expected_type_and_name( + &self, + name_like: &ast::NameLike, + ) -> (Option<Type>, Option<NameOrNameRef>) { let mut node = match self.token.parent() { Some(it) => it, None => return (None, None), }; + + let strip_refs = |mut ty: Type| match name_like { + ast::NameLike::NameRef(n) => { + let p = match n.syntax().parent() { + Some(it) => it, + None => return ty, + }; + let top_syn = match_ast! { + match p { + ast::FieldExpr(e) => e + .syntax() + .ancestors() + .map_while(ast::FieldExpr::cast) + .last() + .map(|it| it.syntax().clone()), + ast::PathSegment(e) => e + .syntax() + .ancestors() + .skip(1) + .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind())) + .find_map(ast::PathExpr::cast) + .map(|it| it.syntax().clone()), + _ => None + } + }; + let top_syn = match top_syn { + Some(it) => it, + None => return ty, + }; + for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) { + cov_mark::hit!(expected_type_fn_param_ref); + ty = ty.strip_reference(); + } + ty + } + _ => ty, + }; + loop { break match_ast! { match node { @@ -199,13 +240,9 @@ impl<'a> CompletionContext<'a> { self.token.clone(), ).map(|ap| { let name = ap.ident().map(NameOrNameRef::Name); - let ty = if has_ref(&self.token) { - cov_mark::hit!(expected_type_fn_param_ref); - ap.ty.remove_ref() - } else { - Some(ap.ty) - }; - (ty, name) + + let ty = strip_refs(ap.ty); + (Some(ty), name) }) .unwrap_or((None, None)) }, @@ -330,8 +367,6 @@ impl<'a> CompletionContext<'a> { return None; } - (self.expected_type, self.expected_name) = self.expected_type_and_name(); - // Overwrite the path kind for derives if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some(ast::NameLike::NameRef(name_ref)) = @@ -389,6 +424,7 @@ impl<'a> CompletionContext<'a> { return Some(analysis); } }; + (self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like); let analysis = match name_like { ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime( Self::classify_lifetime(&self.sema, original_file, lifetime)?, @@ -1141,19 +1177,6 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> { Some((use_tree.path()?, true)) } -fn has_ref(token: &SyntaxToken) -> bool { - let mut token = token.clone(); - for skip in [SyntaxKind::IDENT, SyntaxKind::WHITESPACE, T![mut]] { - if token.kind() == skip { - token = match token.prev_token() { - Some(it) => it, - None => return false, - } - } - } - token.kind() == T![&] -} - pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool { // oh my ... (|| { diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index c5557bdafb339..50845b3881f43 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -391,3 +391,23 @@ fn foo($0: Foo) {} expect![[r#"ty: ?, name: ?"#]], ); } + +#[test] +fn expected_type_ref_prefix_on_field() { + check_expected_type_and_name( + r#" +fn foo(_: &mut i32) {} +struct S { + field: i32, +} + +fn main() { + let s = S { + field: 100, + }; + foo(&mut s.f$0); +} +"#, + expect!["ty: i32, name: ?"], + ); +} From 349dfc7e95c7b384a3e520db3cc26645918857d2 Mon Sep 17 00:00:00 2001 From: hi-rustin <rustin.liu@gmail.com> Date: Wed, 27 Jul 2022 20:18:00 +0800 Subject: [PATCH 07/28] Find original ast node before compute ref match in fn render Signed-off-by: hi-rustin <rustin.liu@gmail.com> --- crates/ide-completion/src/render/function.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 241de0a1834a0..4b5535718c5df 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -85,7 +85,9 @@ fn render( item.ref_match(ref_match, path_ctx.path.syntax().text_range().start()); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - item.ref_match(ref_match, receiver.syntax().text_range().start()); + if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) { + item.ref_match(ref_match, original_expr.syntax().text_range().start()); + } } _ => (), } From bf893d59b54eb3b0dd3bcf34a66f9f0703753ffb Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 27 Jul 2022 17:14:50 +0200 Subject: [PATCH 08/28] internal: Assume condition/iterable is missing if there is only a BlockExpr --- crates/syntax/src/ast/generated/nodes.rs | 4 --- crates/syntax/src/ast/node_ext.rs | 38 ++++++++++++++++++++++++ crates/syntax/src/tests/sourcegen_ast.rs | 2 ++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index cf90ba64cff1a..63309a155219e 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -880,7 +880,6 @@ impl ForExpr { pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) } pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) } - pub fn iterable(&self) -> Option<Expr> { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -890,7 +889,6 @@ pub struct IfExpr { impl ast::HasAttrs for IfExpr {} impl IfExpr { pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) } - pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) } pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) } } @@ -1051,7 +1049,6 @@ pub struct WhileExpr { impl ast::HasAttrs for WhileExpr {} impl WhileExpr { pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) } - pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1170,7 +1167,6 @@ pub struct MatchGuard { } impl MatchGuard { pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) } - pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index b143df1f83f2a..bb92c51e9a90e 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -806,6 +806,19 @@ impl ast::GenericParamList { } } +impl ast::ForExpr { + pub fn iterable(&self) -> Option<ast::Expr> { + // If the iterable is a BlockExpr, check if the body is missing. + // If it is assume the iterable 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, + } + } +} + impl ast::HasLoopBody for ast::ForExpr { fn loop_body(&self) -> Option<ast::BlockExpr> { let mut exprs = support::children(self.syntax()); @@ -815,6 +828,19 @@ impl ast::HasLoopBody for ast::ForExpr { } } +impl ast::WhileExpr { + pub fn condition(&self) -> Option<ast::Expr> { + // If the condition is a BlockExpr, check if the 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, + } + } +} + impl ast::HasLoopBody for ast::WhileExpr { fn loop_body(&self) -> Option<ast::BlockExpr> { let mut exprs = support::children(self.syntax()); @@ -835,3 +861,15 @@ impl From<ast::Adt> for ast::Item { } } } + +impl ast::IfExpr { + pub fn condition(&self) -> Option<ast::Expr> { + support::child(&self.syntax) + } +} + +impl ast::MatchGuard { + pub fn condition(&self) -> Option<ast::Expr> { + support::child(&self.syntax) + } +} diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index 4cfb8075cb155..6d2766225103f 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -682,6 +682,8 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r | "value" | "trait" | "self_ty" + | "iterable" + | "condition" ); if manually_implemented { return; From f83738e1d993d84c1607fcabdf9f41ab00f83d55 Mon Sep 17 00:00:00 2001 From: Brennan Vincent <brennan@umanwizard.com> Date: Wed, 27 Jul 2022 14:34:46 -0400 Subject: [PATCH 09/28] Use large stack on expander thread --- crates/proc-macro-srv/src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 4b1858b8ed89e..4c205b9cadac3 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -39,6 +39,8 @@ pub(crate) struct ProcMacroSrv { expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, } +const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; + impl ProcMacroSrv { pub fn expand(&mut self, task: ExpandMacro) -> Result<FlatTree, PanicMessage> { let expander = self.expander(task.lib.as_ref()).map_err(|err| { @@ -66,13 +68,18 @@ impl ProcMacroSrv { // FIXME: replace this with std's scoped threads once they stabilize // (then remove dependency on crossbeam) let result = crossbeam::scope(|s| { - let res = s + let res = match s + .builder() + .stack_size(EXPANDER_STACK_SIZE) + .name(task.macro_name.clone()) .spawn(|_| { expander .expand(&task.macro_name, ¯o_body, attributes.as_ref()) .map(|it| FlatTree::new(&it)) - }) - .join(); + }) { + Ok(handle) => handle.join(), + Err(e) => std::panic::resume_unwind(Box::new(e)), + }; match res { Ok(res) => res, From e782e59d3de3d4a58cbc8005fd9521502b8d9a61 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 28 Jul 2022 10:05:21 +0200 Subject: [PATCH 10/28] fix: Calculate completions after type anchors --- crates/hir/src/semantics.rs | 18 ++++++ .../src/completions/attribute.rs | 2 +- .../src/completions/attribute/derive.rs | 2 +- crates/ide-completion/src/completions/expr.rs | 23 +++++++- .../src/completions/item_list.rs | 2 +- .../ide-completion/src/completions/pattern.rs | 2 +- crates/ide-completion/src/completions/type.rs | 18 +++++- crates/ide-completion/src/completions/use_.rs | 2 +- crates/ide-completion/src/completions/vis.rs | 2 +- crates/ide-completion/src/context.rs | 5 +- crates/ide-completion/src/context/analysis.rs | 56 ++++++++++--------- crates/ide-completion/src/tests/special.rs | 55 +++++++++++++++++- 12 files changed, 151 insertions(+), 36 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index a75e5cafd0bd1..c84318b2fb877 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -324,6 +324,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_type(ty) } + pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> { + self.imp.resolve_trait(trait_) + } + // FIXME: Figure out a nice interface to inspect adjustments pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> { self.imp.is_implicit_reborrow(expr) @@ -1014,6 +1018,20 @@ impl<'db> SemanticsImpl<'db> { Some(Type::new_with_resolver(self.db, &analyze.resolver, ty)) } + fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> { + let analyze = self.analyze(path.syntax())?; + 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())? + { + TypeNs::TraitId(id) => Some(Trait { id }), + _ => None, + } + } + fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> { self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr) } diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 1d8a8c5f20db3..d9fe94cb44ee1 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -115,7 +115,7 @@ pub(crate) fn complete_attribute_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } let attributes = annotated_item_kind.and_then(|kind| { diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index 14538fef6072c..793c22630bf89 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -97,7 +97,7 @@ pub(crate) fn complete_derive_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index bdf6e64f09696..d4f2766602fbf 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -46,11 +46,32 @@ pub(crate) fn complete_expr_path( }; match qualified { - Qualified::Infer => ctx + Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), + Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) + } + Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + cov_mark::hit!(completes_variant_through_alias); + acc.add_enum_variants(ctx, path_ctx, e); + } + + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 4e4c9fba6cc57..60d05ae46b916 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -66,7 +66,7 @@ pub(crate) fn complete_item_list( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::No | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 17dfe432b3529..af8a0853313b4 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -180,6 +180,6 @@ pub(crate) fn complete_pattern_path( acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 87a998dfcce6b..8f9db2f94c204 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -49,11 +49,27 @@ pub(crate) fn complete_type_path( }; match qualified { - Qualified::Infer => ctx + Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), + Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) + } + Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index bb2ecc9fdde76..2555c34aa7477 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -115,6 +115,6 @@ pub(crate) fn complete_use_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { resolution: None, .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {} } } diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs index ca8303906a800..5e6cf4bf9a521 100644 --- a/crates/ide-completion/src/completions/vis.rs +++ b/crates/ide-completion/src/completions/vis.rs @@ -29,7 +29,7 @@ pub(crate) fn complete_vis_path( acc.add_super_keyword(ctx, *super_chain_len); } - Qualified::Absolute | Qualified::Infer | Qualified::With { .. } => {} + Qualified::Absolute | Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} Qualified::No => { if !has_in_token { cov_mark::hit!(kw_completion_in); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 93b6ad5d145df..e35f79d2b6951 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -193,7 +193,10 @@ pub(super) enum Qualified { super_chain_len: Option<usize>, }, /// <_>:: - Infer, + TypeAnchor { + ty: Option<hir::Type>, + trait_: Option<hir::Trait>, + }, /// Whether the path is an absolute path Absolute, } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 09a1a99eb64ca..3e7e637dd9ed5 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -920,49 +920,53 @@ impl<'a> CompletionContext<'a> { path_ctx.has_type_args = segment.generic_arg_list().is_some(); // calculate the qualifier context - if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { + if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) { path_ctx.use_tree_parent = use_tree_parent; if !use_tree_parent && segment.coloncolon_token().is_some() { path_ctx.qualified = Qualified::Absolute; } else { - let path = path + let qualifier = qualifier .segment() .and_then(|it| find_node_in_file(original_file, &it)) .map(|it| it.parent_path()); - if let Some(path) = path { - // `<_>::$0` - let is_infer_qualifier = path.qualifier().is_none() - && matches!( - path.segment().and_then(|it| it.kind()), - Some(ast::PathSegmentKind::Type { - type_ref: Some(ast::Type::InferType(_)), - trait_ref: None, - }) - ); + if let Some(qualifier) = qualifier { + let type_anchor = match qualifier.segment().and_then(|it| it.kind()) { + Some(ast::PathSegmentKind::Type { + type_ref: Some(type_ref), + trait_ref, + }) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)), + _ => None, + }; - path_ctx.qualified = if is_infer_qualifier { - Qualified::Infer + path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor { + let ty = match ty { + ast::Type::InferType(_) => None, + ty => sema.resolve_type(&ty), + }; + let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?)); + Qualified::TypeAnchor { ty, trait_ } } else { - let res = sema.resolve_path(&path); + let res = sema.resolve_path(&qualifier); // For understanding how and why super_chain_len is calculated the way it // is check the documentation at it's definition let mut segment_count = 0; - let super_count = iter::successors(Some(path.clone()), |p| p.qualifier()) - .take_while(|p| { - p.segment() - .and_then(|s| { - segment_count += 1; - s.super_token() - }) - .is_some() - }) - .count(); + let super_count = + iter::successors(Some(qualifier.clone()), |p| p.qualifier()) + .take_while(|p| { + p.segment() + .and_then(|s| { + segment_count += 1; + s.super_token() + }) + .is_some() + }) + .count(); let super_chain_len = if segment_count > super_count { None } else { Some(super_count) }; - Qualified::With { path, resolution: res, super_chain_len } + Qualified::With { path: qualifier, resolution: res, super_chain_len } } }; } diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index ca779c2fc713e..033dc99c26cf0 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -674,7 +674,60 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self "#]], - ) + ); +} + +#[test] +fn type_anchor_type() { + check( + r#" +trait Foo { + fn foo() -> Self; +} +struct Bar; +impl Bar { + fn bar() {} +} +impl Foo for Bar { + fn foo() -> { + Bar + } +} +fn bar() -> Bar { + <Bar>::$0 +} +"#, + expect![[r#" + fn bar() fn() + fn foo() (as Foo) fn() -> Self + "#]], + ); +} + +#[test] +fn type_anchor_type_trait() { + check( + r#" +trait Foo { + fn foo() -> Self; +} +struct Bar; +impl Bar { + fn bar() {} +} +impl Foo for Bar { + fn foo() -> { + Bar + } +} +fn bar() -> Bar { + <Bar as Foo>::$0 +} +"#, + expect![[r#" + fn foo() (as Foo) fn() -> Self + "#]], + ); } #[test] From 7c59d7c75c1aed160bf1aa67047ba6063ae21ba0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 28 Jul 2022 15:47:46 +0200 Subject: [PATCH 11/28] fix: Fix pattern completions adding unnecessary braces --- crates/ide-completion/src/completions.rs | 13 +++++++++-- .../ide-completion/src/completions/pattern.rs | 2 +- crates/ide-completion/src/render/pattern.rs | 23 +++++++++++++++---- crates/ide-completion/src/tests/pattern.rs | 4 ++-- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 149afcac9d478..72579e6026aee 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -400,7 +400,7 @@ impl Completions { ) { if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { cov_mark::hit!(enum_variant_pattern_path); - self.add_variant_pat(ctx, pat_ctx, variant, local_name); + self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name); return; } @@ -484,12 +484,14 @@ impl Completions { &mut self, ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, + path_ctx: Option<&PathCompletionCtx>, variant: hir::Variant, local_name: Option<hir::Name>, ) { self.add_opt(render_variant_pat( RenderContext::new(ctx), pattern_ctx, + path_ctx, variant, local_name.clone(), None, @@ -504,7 +506,14 @@ impl Completions { path: hir::ModPath, ) { let path = Some(&path); - self.add_opt(render_variant_pat(RenderContext::new(ctx), pattern_ctx, variant, None, path)); + self.add_opt(render_variant_pat( + RenderContext::new(ctx), + pattern_ctx, + None, + variant, + None, + path, + )); } pub(crate) fn add_struct_pat( diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index af8a0853313b4..71d2d9d434b44 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -74,7 +74,7 @@ pub(crate) fn complete_pattern( hir::ModuleDef::Variant(variant) if refutable || single_variant_enum(variant.parent_enum(ctx.db)) => { - acc.add_variant_pat(ctx, pattern_ctx, variant, Some(name.clone())); + acc.add_variant_pat(ctx, pattern_ctx, None, variant, Some(name.clone())); true } hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 03db08a911e92..34a384f2f7ae8 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use syntax::SmolStr; use crate::{ - context::{ParamContext, ParamKind, PatternContext}, + context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext}, render::{ variant::{format_literal_label, visible_fields}, RenderContext, @@ -42,6 +42,7 @@ pub(crate) fn render_struct_pat( pub(crate) fn render_variant_pat( ctx: RenderContext<'_>, pattern_ctx: &PatternContext, + path_ctx: Option<&PathCompletionCtx>, variant: hir::Variant, local_name: Option<Name>, path: Option<&hir::ModPath>, @@ -58,9 +59,23 @@ pub(crate) fn render_variant_pat( (name.to_smol_str(), name.escaped().to_smol_str()) } }; - let kind = variant.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind); - let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; + + let (label, pat) = match path_ctx { + Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()), + _ => { + let kind = variant.kind(ctx.db()); + let label = format_literal_label(name.as_str(), kind); + let pat = render_pat( + &ctx, + pattern_ctx, + &escaped_name, + kind, + &visible_fields, + fields_omitted, + )?; + (label, pat) + } + }; Some(build_completion(ctx, label, pat, variant)) } diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 877b5f2164331..30ddbe2dc6f60 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -443,7 +443,7 @@ fn foo() { } "#, expect![[r#" - bn TupleVariant(…) TupleVariant($1)$0 + bn TupleVariant TupleVariant "#]], ); check_empty( @@ -458,7 +458,7 @@ fn foo() { } "#, expect![[r#" - bn RecordVariant {…} RecordVariant { field$1 }$0 + bn RecordVariant RecordVariant "#]], ); } From 74abd44a265d6daded813ccb48ed599d835ea532 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 28 Jul 2022 17:09:31 +0200 Subject: [PATCH 12/28] fix: Do completions in path qualifier position --- crates/ide-completion/src/completions/expr.rs | 16 +- crates/ide-completion/src/context/analysis.rs | 187 ++++++++++-------- 2 files changed, 118 insertions(+), 85 deletions(-) diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index d4f2766602fbf..bafaeb502ad75 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext<'_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, - &ExprCtx { + expr_ctx: &ExprCtx, +) { + let _p = profile::span("complete_expr_path"); + if !ctx.qualifier_ctx.none() { + return; + } + + let &ExprCtx { in_block_expr, in_loop_body, after_if_expr, @@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path( ref impl_, in_match_guard, .. - }: &ExprCtx, -) { - let _p = profile::span("complete_expr_path"); - if !ctx.qualifier_ctx.none() { - return; - } + } = expr_ctx; let wants_mut_token = ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false); diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 3e7e637dd9ed5..76fc74c01d22c 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -592,7 +592,7 @@ impl<'a> CompletionContext<'a> { has_call_parens: false, has_macro_bang: false, qualified: Qualified::No, - parent: path.parent_path(), + parent: None, path: path.clone(), kind: PathKind::Item { kind: ItemListKind::SourceFile }, has_type_args: false, @@ -827,92 +827,123 @@ impl<'a> CompletionContext<'a> { PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } }; + let mut kind_macro_call = |it: ast::MacroCall| { + path_ctx.has_macro_bang = it.excl_token().is_some(); + let parent = it.syntax().parent()?; + // Any path in an item list will be treated as a macro call by the parser + let kind = match_ast! { + match parent { + ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), + ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, + ast::MacroType(ty) => make_path_kind_type(ty.into()), + ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, + ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { + Some(it) => match_ast! { + match it { + ast::Trait(_) => ItemListKind::Trait, + ast::Impl(it) => if it.trait_().is_some() { + ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) + } else { + ItemListKind::Impl + }, + _ => return None + } + }, + None => return None, + } }, + ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, + ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, + _ => return None, + } + }; + Some(kind) + }; + let make_path_kind_attr = |meta: ast::Meta| { + let attr = meta.parent_attr()?; + let kind = attr.kind(); + let attached = attr.syntax().parent()?; + let is_trailing_outer_attr = kind != AttrKind::Inner + && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next) + .is_none(); + let annotated_item_kind = + if is_trailing_outer_attr { None } else { Some(attached.kind()) }; + Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } }) + }; + // Infer the path kind let parent = path.syntax().parent()?; let kind = match_ast! { - match parent { - ast::PathType(it) => make_path_kind_type(it.into()), - ast::PathExpr(it) => { - if let Some(p) = it.syntax().parent() { - if ast::ExprStmt::can_cast(p.kind()) { - if let Some(kind) = inbetween_body_and_decl_check(p) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + match parent { + ast::PathType(it) => make_path_kind_type(it.into()), + ast::PathExpr(it) => { + if let Some(p) = it.syntax().parent() { + if ast::ExprStmt::can_cast(p.kind()) { + if let Some(kind) = inbetween_body_and_decl_check(p) { + return Some(make_res(NameRefKind::Keyword(kind))); } } + } - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); - make_path_kind_expr(it.into()) - }, - ast::TupleStructPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::RecordPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::MacroCall(it) => { - // A macro call in this position is usually a result of parsing recovery, so check that - if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + make_path_kind_expr(it.into()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + // A macro call in this position is usually a result of parsing recovery, so check that + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + return Some(make_res(NameRefKind::Keyword(kind))); + } - path_ctx.has_macro_bang = it.excl_token().is_some(); - let parent = it.syntax().parent()?; - // Any path in an item list will be treated as a macro call by the parser - match_ast! { - match parent { - ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), - ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, - ast::MacroType(ty) => make_path_kind_type(ty.into()), - ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, - ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { - Some(it) => match_ast! { - match it { - ast::Trait(_) => ItemListKind::Trait, - ast::Impl(it) => if it.trait_().is_some() { - ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) - } else { - ItemListKind::Impl - }, - _ => return None - } - }, - None => return None, - } }, - ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, - ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, - _ => return None, - } - } - }, - ast::Meta(meta) => { - let attr = meta.parent_attr()?; - let kind = attr.kind(); - let attached = attr.syntax().parent()?; - let is_trailing_outer_attr = kind != AttrKind::Inner - && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none(); - let annotated_item_kind = if is_trailing_outer_attr { - None - } else { - Some(attached.kind()) - }; - PathKind::Attr { - attr_ctx: AttrCtx { - kind, - annotated_item_kind, - } + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + // completing inside a qualifier + ast::Path(parent) => { + path_ctx.parent = Some(parent.clone()); + let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?; + match_ast! { + match parent { + ast::PathType(it) => make_path_kind_type(it.into()), + ast::PathExpr(it) => { + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + + make_path_kind_expr(it.into()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + _ => return None, } - }, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, - ast::UseTree(_) => PathKind::Use, - _ => return None, - + } + }, + _ => return None, } }; From 8658425a672e5e56ac5e02db8423ca56c81c2c31 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara <pavan.sss1991@gmail.com> Date: Thu, 28 Jul 2022 16:21:14 +0100 Subject: [PATCH 13/28] publish: Use cargo ws rename to rename crates --- .github/workflows/publish.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 927996c1bef21..a4497f49e3c2f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,8 +34,21 @@ jobs: git config --global user.email "runner@gha.local" git config --global user.name "Github Action" rm Cargo.lock + # Fix names for crates that were published before switch to kebab-case. + cargo workspaces rename --from base-db base_db + cargo workspaces rename --from hir-def hir_def + cargo workspaces rename --from hir-expand hir_expand + cargo workspaces rename --from hir-ty hir_ty + cargo workspaces rename --from ide-assists ide_assists + cargo workspaces rename --from ide-completion ide_completion + cargo workspaces rename --from ide-db ide_db + cargo workspaces rename --from ide-diagnostics ide_diagnostics + cargo workspaces rename --from ide-ssr ide_ssr + cargo workspaces rename --from proc-macro-api proc_macro_api + cargo workspaces rename --from proc-macro-srv proc_macro_srv + cargo workspaces rename --from project-model project_model + cargo workspaces rename --from test-utils test_utils + cargo workspaces rename --from text-edit text_edit cargo workspaces rename ra_ap_%n find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} + - # Fix names for crates that were published before switch to kebab-case. - find crates -name 'Cargo.toml' -exec sed -i "s/ra_ap_base-db/ra_ap_base_db/g; s/ra_ap_hir-def/ra_ap_hir_def/g; s/ra_ap_hir-expand/ra_ap_hir_expand/g; s/ra_ap_hir-ty/ra_ap_hir_ty/g; s/ra_ap_ide-assists/ra_ap_ide_assists/g; s/ra_ap_ide-completion/ra_ap_ide_completion/g; s/ra_ap_ide-db/ra_ap_ide_db/g; s/ra_ap_ide-diagnostics/ra_ap_ide_diagnostics/g; s/ra_ap_ide-ssr/ra_ap_ide_ssr/g; s/ra_ap_proc-macro-api/ra_ap_proc_macro_api/g; s/ra_ap_proc-macro-srv/ra_ap_proc_macro_srv/g; s/ra_ap_project-model/ra_ap_project_model/g; s/ra_ap_test-utils/ra_ap_test_utils/g; s/ra_ap_text-edit/ra_ap_text_edit/g" {} + cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$PATCH From ce7541260d6a5c76633b4c0e2e1639730018773d Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 28 Jul 2022 17:49:23 +0200 Subject: [PATCH 14/28] fix: Don't complete marker traits in expression position --- crates/ide-completion/src/completions/expr.rs | 17 ++++++++++++++--- crates/ide-completion/src/render.rs | 3 --- crates/ide-completion/src/tests/expression.rs | 2 -- crates/ide-completion/src/tests/record.rs | 1 - 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index bafaeb502ad75..5d0ddaaf2a228 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -202,10 +202,21 @@ pub(crate) fn complete_expr_path( } } } - ctx.process_all_names(&mut |name, def| { - if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + ctx.process_all_names(&mut |name, def| match def { + ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => { + let assocs = t.items_with_supertraits(ctx.db); + match &*assocs { + // traits with no assoc items are unusable as expressions since + // there is no associated item path that can be constructed with them + [] => (), + // FIXME: Render the assoc item with the trait qualified + &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def), + // FIXME: Append `::` to the thing here, since a trait on its own won't work + [..] => acc.add_path_resolution(ctx, path_ctx, name, def), + } } + _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def), + _ => (), }); if is_func_update.is_none() { diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 9b25964a6086e..39cf957137937 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -1347,7 +1347,6 @@ fn main() { fn main() [] fn foo(…) [] md core [] - tt Sized [] "#]], ) } @@ -1394,7 +1393,6 @@ fn main() { fn main() [] fn foo(…) [] md core [] - tt Sized [] "#]], ) } @@ -1492,7 +1490,6 @@ fn main() { fn &bar() [type] fn foo(…) [] md core [] - tt Sized [] "#]], ) } diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index ce9d01d337bae..ce7a543d9f300 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -44,7 +44,6 @@ fn baz() { st Record st Tuple st Unit - tt Trait un Union ev TupleV(…) TupleV(u32) bt u32 @@ -137,7 +136,6 @@ impl Unit { st Record st Tuple st Unit - tt Trait tp TypeParam un Union ev TupleV(…) TupleV(u32) diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index ec32602fa3c2f..f6accc68e5e80 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -167,7 +167,6 @@ fn main() { st Foo st Foo {…} Foo { foo1: u32, foo2: u32 } tt Default - tt Sized bt u32 kw crate:: kw self:: From f867ddc6217438ad514980fcbd5f59e2d99c3d55 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 28 Jul 2022 18:49:31 +0200 Subject: [PATCH 15/28] fix: Order ItemScope::entries results --- crates/hir-def/src/item_scope.rs | 10 ++++----- .../src/handlers/expand_glob_import.rs | 10 ++++----- crates/ide-assists/src/tests/generated.rs | 2 +- crates/ide-completion/src/render.rs | 22 +++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 579f803ea193a..a11a92204c15c 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -5,6 +5,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; use hir_expand::{name::Name, AstId, MacroCallId}; +use itertools::Itertools; use once_cell::sync::Lazy; use profile::Count; use rustc_hash::{FxHashMap, FxHashSet}; @@ -97,15 +98,14 @@ pub(crate) enum BuiltinShadowMode { impl ItemScope { pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { // FIXME: shadowing - let keys: FxHashSet<_> = self - .types + self.types .keys() .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .collect(); - - keys.into_iter().map(move |name| (name, self.get(name))) + .sorted() + .unique() + .map(move |name| (name, self.get(name))) } pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 943c1d90e6365..87f5018fb6958 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -36,7 +36,7 @@ use crate::{ // pub struct Baz; // } // -// use foo::{Baz, Bar}; +// use foo::{Bar, Baz}; // // fn qux(bar: Bar, baz: Baz) {} // ``` @@ -281,7 +281,7 @@ mod foo { pub fn f() {} } -use foo::{Baz, Bar, f}; +use foo::{Bar, Baz, f}; fn qux(bar: Bar, baz: Baz) { f(); @@ -351,7 +351,7 @@ mod foo { pub fn f() {} } -use foo::{Baz, Bar, f}; +use foo::{Bar, Baz, f}; fn qux(bar: Bar, baz: Baz) { f(); @@ -440,7 +440,7 @@ mod foo { } } -use foo::{bar::{Baz, Bar, f}, baz::*}; +use foo::{bar::{Bar, Baz, f}, baz::*}; fn qux(bar: Bar, baz: Baz) { f(); @@ -561,7 +561,7 @@ mod foo { use foo::{ bar::{*, f}, - baz::{g, qux::{q, h}} + baz::{g, qux::{h, q}} }; fn qux(bar: Bar, baz: Baz) { diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index e8d48607be0e9..6eaab48a32ba5 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -535,7 +535,7 @@ mod foo { pub struct Baz; } -use foo::{Baz, Bar}; +use foo::{Bar, Baz}; fn qux(bar: Bar, baz: Baz) {} "#####, diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 39cf957137937..946134b0ff95d 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -1271,8 +1271,8 @@ fn main() { st S [] st &mut S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); check_relevance( @@ -1288,8 +1288,8 @@ fn main() { lc s [type+name+local] st S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); check_relevance( @@ -1305,8 +1305,8 @@ fn main() { lc ssss [type+local] st S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); } @@ -1342,10 +1342,10 @@ fn main() { lc &t [type+local] st S [] st &S [type] - st T [] st S [] - fn main() [] + st T [] fn foo(…) [] + fn main() [] md core [] "#]], ) @@ -1388,10 +1388,10 @@ fn main() { lc &mut t [type+local] st S [] st &mut S [type] - st T [] st S [] - fn main() [] + st T [] fn foo(…) [] + fn main() [] md core [] "#]], ) @@ -1483,12 +1483,12 @@ fn main() { expect![[r#" st S [] st &S [type] - st T [] st S [] - fn main() [] + st T [] fn bar() [] fn &bar() [type] fn foo(…) [] + fn main() [] md core [] "#]], ) @@ -1633,8 +1633,8 @@ fn foo() { ev Foo::B [type_could_unify] fn foo() [] en Foo [] - fn baz() [] fn bar() [] + fn baz() [] "#]], ); } @@ -1724,9 +1724,9 @@ fn f() { } "#, expect![[r#" - md std [] st Buffer [] fn f() [] + md std [] tt BufRead (use std::io::BufRead) [requires_import] st BufReader (use std::io::BufReader) [requires_import] st BufWriter (use std::io::BufWriter) [requires_import] From 948c9afc73786d5b4895fa4ea9041992601a59c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Thu, 28 Jul 2022 21:02:36 +0300 Subject: [PATCH 16/28] Only run rainbow highlighting test on 64-bit Unix --- crates/ide/src/syntax_highlighting/tests.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6ba6153178da9..d21bfc938514c 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -915,6 +915,10 @@ fn main() { } #[test] +#[cfg_attr( + all(unix, not(target_pointer_width = "64")), + ignore = "depends on `DefaultHasher` outputs" +)] fn test_rainbow_highlighting() { check_highlighting( r#" From 11ef494b3764811b8ea5178d6c9bd67b287f104a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Thu, 28 Jul 2022 21:45:47 +0300 Subject: [PATCH 17/28] Be more explicit when filtering built-in completions --- crates/ide-completion/src/tests.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 4be6acbe8461e..cf826648dcf7b 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -23,8 +23,6 @@ mod type_pos; mod use_tree; mod visibility; -use std::mem; - use hir::{db::DefDatabase, PrefixKind, Semantics}; use ide_db::{ base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, @@ -107,12 +105,9 @@ fn completion_list_with_config( ) -> String { // filter out all but one builtintype completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); - let mut bt_seen = false; let items = items .into_iter() - .filter(|it| { - it.kind() != CompletionItemKind::BuiltinType || !mem::replace(&mut bt_seen, true) - }) + .filter(|it| it.kind() != CompletionItemKind::BuiltinType || it.label() == "u32") .filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword) .filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet) .sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned))) From 902fd6ddcdd4e3ad03c7a9099f0b3b1d7e6a95e8 Mon Sep 17 00:00:00 2001 From: cynecx <me@cynecx.net> Date: Fri, 29 Jul 2022 02:27:16 +0200 Subject: [PATCH 18/28] fix: complete path of existing record expr --- crates/ide-completion/src/context/analysis.rs | 2 ++ crates/ide-completion/src/tests/expression.rs | 19 +++++++++++++++++++ crates/parser/src/grammar/paths.rs | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 76fc74c01d22c..22ec7cead4988 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -939,10 +939,12 @@ impl<'a> CompletionContext<'a> { ast::Meta(meta) => make_path_kind_attr(meta)?, ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, ast::UseTree(_) => PathKind::Use, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), _ => return None, } } }, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), _ => return None, } }; diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index ce7a543d9f300..925081ebf6602 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -651,3 +651,22 @@ fn main() { "]], ); } + +#[test] +fn complete_record_expr_path() { + check( + r#" +struct Zulu; +impl Zulu { + fn test() -> Self { } +} +fn boi(val: Zulu) { } +fn main() { + boi(Zulu:: $0 {}); +} +"#, + expect![[r#" + fn test() fn() -> Zulu + "#]], + ); +} diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index f9efcef92a610..8de5d33a1936d 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -54,7 +54,7 @@ fn path_for_qualifier( mut qual: CompletedMarker, ) -> CompletedMarker { loop { - let use_tree = matches!(p.nth(2), T![*] | T!['{']); + let use_tree = mode == Mode::Use && matches!(p.nth(2), T![*] | T!['{']); if p.at(T![::]) && !use_tree { let path = qual.precede(p); p.bump(T![::]); From 5b0ec1ebe4da106c18ce1ceec76e4adc627bddd1 Mon Sep 17 00:00:00 2001 From: Alex Gaynor <alex.gaynor@gmail.com> Date: Fri, 29 Jul 2022 23:26:00 -0400 Subject: [PATCH 19/28] parallelize HTML checking tool --- Cargo.lock | 1 + src/tools/html-checker/Cargo.toml | 1 + src/tools/html-checker/main.rs | 46 +++++++++++++++++-------------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58c3982de2333..ccffbb01518dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1788,6 +1788,7 @@ dependencies = [ name = "html-checker" version = "0.1.0" dependencies = [ + "rayon", "walkdir", ] diff --git a/src/tools/html-checker/Cargo.toml b/src/tools/html-checker/Cargo.toml index 34d3954db28dc..72d61d9bd2638 100644 --- a/src/tools/html-checker/Cargo.toml +++ b/src/tools/html-checker/Cargo.toml @@ -9,3 +9,4 @@ path = "main.rs" [dependencies] walkdir = "2" +rayon = "1.5" diff --git a/src/tools/html-checker/main.rs b/src/tools/html-checker/main.rs index f52fbdfe2d7dc..9b4d2c5259806 100644 --- a/src/tools/html-checker/main.rs +++ b/src/tools/html-checker/main.rs @@ -1,3 +1,4 @@ +use rayon::iter::{ParallelBridge, ParallelIterator}; use std::env; use std::path::Path; use std::process::{Command, Output}; @@ -56,27 +57,30 @@ const DOCS_TO_CHECK: &[&str] = // Returns the number of files read and the number of errors. fn find_all_html_files(dir: &Path) -> (usize, usize) { - let mut files_read = 0; - let mut errors = 0; - - for entry in walkdir::WalkDir::new(dir).into_iter().filter_entry(|e| { - e.depth() != 1 - || e.file_name() - .to_str() - .map(|s| DOCS_TO_CHECK.into_iter().any(|d| *d == s)) - .unwrap_or(false) - }) { - let entry = entry.expect("failed to read file"); - if !entry.file_type().is_file() { - continue; - } - let entry = entry.path(); - if entry.extension().and_then(|s| s.to_str()) == Some("html") { - errors += check_html_file(&entry); - files_read += 1; - } - } - (files_read, errors) + walkdir::WalkDir::new(dir) + .into_iter() + .filter_entry(|e| { + e.depth() != 1 + || e.file_name() + .to_str() + .map(|s| DOCS_TO_CHECK.into_iter().any(|d| *d == s)) + .unwrap_or(false) + }) + .par_bridge() + .map(|entry| { + let entry = entry.expect("failed to read file"); + if !entry.file_type().is_file() { + return (0, 0); + } + let entry = entry.path(); + // (Number of files processed, number of errors) + if entry.extension().and_then(|s| s.to_str()) == Some("html") { + (1, check_html_file(&entry)) + } else { + (0, 0) + } + }) + .reduce(|| (0, 0), |a, b| (a.0 + b.0, a.1 + b.1)) } /// Default `tidy` command for macOS is too old that it does not have `mute-id` and `mute` options. From 618cfd792ca2b3da5cd027bad20a7769f5d9f973 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sat, 30 Jul 2022 09:43:30 +0200 Subject: [PATCH 20/28] fix: Fix ast-id up when merging raw attributes --- crates/hir-def/src/attr.rs | 21 ++++++-- crates/ide/src/syntax_highlighting.rs | 4 +- crates/ide/src/syntax_highlighting/inject.rs | 9 ++-- .../highlight_module_docs_inline.html | 51 +++++++++++++++++++ .../highlight_module_docs_outline.html | 50 ++++++++++++++++++ crates/ide/src/syntax_highlighting/tests.rs | 42 +++++++++++++++ 6 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html create mode 100644 crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 8a6b6f3effd2e..2b39c6f8da86b 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -124,13 +124,24 @@ impl RawAttrs { pub(crate) fn merge(&self, other: Self) -> Self { // FIXME: This needs to fixup `AttrId`s - match (&self.entries, &other.entries) { + match (&self.entries, other.entries) { (None, None) => Self::EMPTY, - (Some(entries), None) | (None, Some(entries)) => { - Self { entries: Some(entries.clone()) } - } + (None, entries @ Some(_)) => Self { entries }, + (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } + let last_ast_index = a.last().map_or(0, |it| it.id.ast_index + 1); + Self { + entries: Some( + a.iter() + .cloned() + .chain(b.iter().map(|it| { + let mut it = it.clone(); + it.id.ast_index += last_ast_index; + it + })) + .collect(), + ), + } } } } diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d013d6f4b19ff..3fb49b45d9888 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -13,7 +13,7 @@ mod html; #[cfg(test)] mod tests; -use hir::{InFile, Name, Semantics}; +use hir::{Name, Semantics}; use ide_db::{FxHashMap, RootDatabase}; use syntax::{ ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T, @@ -325,7 +325,7 @@ fn traverse( Leave(NodeOrToken::Node(node)) => { // Doc comment highlighting injection, we do this when leaving the node // so that we overwrite the highlighting of the doc comment itself. - inject::doc_comment(hl, sema, InFile::new(file_id.into(), &node)); + inject::doc_comment(hl, sema, file_id, &node); continue; } }; diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index f779a985a99ae..f376f9fda7a57 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -5,7 +5,8 @@ use std::mem; use either::Either; use hir::{InFile, Semantics}; use ide_db::{ - active_parameter::ActiveParameter, defs::Definition, rust_doc::is_rust_fence, SymbolKind, + active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence, + SymbolKind, }; use syntax::{ ast::{self, AstNode, IsString, QuoteOffsets}, @@ -81,16 +82,18 @@ pub(super) fn ra_fixture( const RUSTDOC_FENCE_LENGTH: usize = 3; const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; -/// Injection of syntax highlighting of doctests. +/// Injection of syntax highlighting of doctests and intra doc links. pub(super) fn doc_comment( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - InFile { file_id: src_file_id, value: node }: InFile<&SyntaxNode>, + src_file_id: FileId, + node: &SyntaxNode, ) { let (attributes, def) = match doc_attributes(sema, node) { Some(it) => it, None => return, }; + let src_file_id = src_file_id.into(); // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html new file mode 100644 index 0000000000000..8a1d69816e688 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -0,0 +1,51 @@ + +<style> +body { margin: 0; } +pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } + +.lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } +.comment { color: #7F9F7F; } +.documentation { color: #629755; } +.intra_doc_link { font-style: italic; } +.injected { opacity: 0.65 ; } +.struct, .enum { color: #7CB8BB; } +.enum_variant { color: #BDE0F3; } +.string_literal { color: #CC9393; } +.field { color: #94BFF3; } +.function { color: #93E0E3; } +.function.unsafe { color: #BC8383; } +.trait.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } +.mutable.unsafe { color: #BC8383; text-decoration: underline; } +.keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } +.parameter { color: #94BFF3; } +.text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.builtin_type { color: #8CD0D3; } +.type_param { color: #DFAF8F; } +.attribute { color: #94BFF3; } +.numeric_literal { color: #BFEBBF; } +.bool_literal { color: #BFE6EB; } +.macro { color: #94BFF3; } +.derive { color: #94BFF3; font-style: italic; } +.module { color: #AFD8AF; } +.value_param { color: #DCDCCC; } +.variable { color: #DCDCCC; } +.format_specifier { color: #CC696B; } +.mutable { text-decoration: underline; } +.escape_sequence { color: #94BFF3; } +.keyword { color: #F0DFAF; font-weight: bold; } +.control { font-style: italic; } +.reference { font-style: italic; font-weight: bold; } + +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +</style> +<pre><code><span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[Struct]</span> +<span class="comment documentation">//! This is an intra doc injection test for modules</span> +<span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[Struct]</span> +<span class="comment documentation">//! This is an intra doc injection test for modules</span> + +<span class="keyword">pub</span> <span class="keyword">struct</span> <span class="struct declaration public">Struct</span><span class="semicolon">;</span> +</code></pre> \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html new file mode 100644 index 0000000000000..c4c3e3dc2606e --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html @@ -0,0 +1,50 @@ + +<style> +body { margin: 0; } +pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } + +.lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } +.comment { color: #7F9F7F; } +.documentation { color: #629755; } +.intra_doc_link { font-style: italic; } +.injected { opacity: 0.65 ; } +.struct, .enum { color: #7CB8BB; } +.enum_variant { color: #BDE0F3; } +.string_literal { color: #CC9393; } +.field { color: #94BFF3; } +.function { color: #93E0E3; } +.function.unsafe { color: #BC8383; } +.trait.unsafe { color: #BC8383; } +.operator.unsafe { color: #BC8383; } +.mutable.unsafe { color: #BC8383; text-decoration: underline; } +.keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } +.parameter { color: #94BFF3; } +.text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.builtin_type { color: #8CD0D3; } +.type_param { color: #DFAF8F; } +.attribute { color: #94BFF3; } +.numeric_literal { color: #BFEBBF; } +.bool_literal { color: #BFE6EB; } +.macro { color: #94BFF3; } +.derive { color: #94BFF3; font-style: italic; } +.module { color: #AFD8AF; } +.value_param { color: #DCDCCC; } +.variable { color: #DCDCCC; } +.format_specifier { color: #CC696B; } +.mutable { text-decoration: underline; } +.escape_sequence { color: #94BFF3; } +.keyword { color: #F0DFAF; font-weight: bold; } +.control { font-style: italic; } +.reference { font-style: italic; font-weight: bold; } + +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +</style> +<pre><code><span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[crate::foo::Struct]</span> +<span class="comment documentation">/// This is an intra doc injection test for modules</span> +<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[crate::foo::Struct]</span> +<span class="comment documentation">/// This is an intra doc injection test for modules</span> +<span class="keyword">mod</span> <span class="module declaration">foo</span><span class="semicolon">;</span> +</code></pre> \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index d21bfc938514c..99be7c6648687 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -914,6 +914,48 @@ fn main() { ); } +#[test] +fn test_mod_hl_injection() { + check_highlighting( + r##" +//- /foo.rs +//! [Struct] +//! This is an intra doc injection test for modules +//! [Struct] +//! This is an intra doc injection test for modules + +pub struct Struct; +//- /lib.rs crate:foo +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +mod foo; +"##, + expect_file!["./test_data/highlight_module_docs_inline.html"], + false, + ); + check_highlighting( + r##" +//- /lib.rs crate:foo +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +mod foo; +//- /foo.rs +//! [Struct] +//! This is an intra doc injection test for modules +//! [Struct] +//! This is an intra doc injection test for modules + +pub struct Struct; +"##, + expect_file!["./test_data/highlight_module_docs_outline.html"], + false, + ); +} + #[test] #[cfg_attr( all(unix, not(target_pointer_width = "64")), From 58c3a5634fec30705be19f21971e016c730f305c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Sun, 31 Jul 2022 20:26:35 +0300 Subject: [PATCH 21/28] Update xtask promote and release instructions --- docs/dev/README.md | 7 ++++--- xtask/src/release.rs | 10 ++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index 468f2b9e981fe..76bbd1e91889e 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -210,7 +210,8 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release` ./rust-rust-analyzer # Note the name! ``` -Additionally, it assumes that the remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). +The remote for `rust-analyzer` must be called `upstream` (I use `origin` to point to my fork). +In addition, for `xtask promote` (see below), `rust-rust-analyzer` must have a `rust-analyzer` remote pointing to this repository on GitHub. `release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. This step uses the `curl` and `jq` applications, which need to be available in `PATH`. @@ -225,13 +226,13 @@ Release steps: * push it to `upstream`. This triggers GitHub Actions which: * runs `cargo xtask dist` to package binaries and VS Code extension * makes a GitHub release - * pushes VS Code extension to the marketplace + * publishes the VS Code extension to the marketplace * call the GitHub API for PR details * create a new changelog in `rust-analyzer.github.io` 3. While the release is in progress, fill in the changelog 4. Commit & push the changelog 5. Tweet -6. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. +6. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree. Self-approve the PR. If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 1c5fc64c2417e..17ada5156407e 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -77,18 +77,12 @@ impl flags::Promote { cmd!(sh, "git switch master").run()?; cmd!(sh, "git fetch upstream").run()?; cmd!(sh, "git reset --hard upstream/master").run()?; - cmd!(sh, "git submodule update --recursive").run()?; let date = date_iso(sh)?; let branch = format!("rust-analyzer-{date}"); cmd!(sh, "git switch -c {branch}").run()?; - { - let _dir = sh.push_dir("src/tools/rust-analyzer"); - cmd!(sh, "git fetch origin").run()?; - cmd!(sh, "git reset --hard origin/release").run()?; - } - cmd!(sh, "git add src/tools/rust-analyzer").run()?; - cmd!(sh, "git commit -m':arrow_up: rust-analyzer'").run()?; + cmd!(sh, "git subtree pull -P src/tools/rust-analyzer rust-analyzer master").run()?; + if !self.dry_run { cmd!(sh, "git push -u origin {branch}").run()?; cmd!( From d31f3605cea39530cb6b5b1c89934b174c886f49 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sun, 31 Jul 2022 19:27:20 +0200 Subject: [PATCH 22/28] Properly cfg the `max` field of Limit --- crates/limit/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/limit/src/lib.rs b/crates/limit/src/lib.rs index 3c1da80edb98b..d6a706a7cd73a 100644 --- a/crates/limit/src/lib.rs +++ b/crates/limit/src/lib.rs @@ -2,12 +2,13 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#[cfg(feature = "tracking")] use std::sync::atomic::AtomicUsize; /// Represents a struct used to enforce a numerical limit. pub struct Limit { upper_bound: usize, - #[allow(unused)] + #[cfg(feature = "tracking")] max: AtomicUsize, } @@ -15,14 +16,22 @@ impl Limit { /// Creates a new limit. #[inline] pub const fn new(upper_bound: usize) -> Self { - Self { upper_bound, max: AtomicUsize::new(0) } + Self { + upper_bound, + #[cfg(feature = "tracking")] + max: AtomicUsize::new(0), + } } /// Creates a new limit. #[inline] #[cfg(feature = "tracking")] pub const fn new_tracking(upper_bound: usize) -> Self { - Self { upper_bound, max: AtomicUsize::new(1) } + Self { + upper_bound, + #[cfg(feature = "tracking")] + max: AtomicUsize::new(1), + } } /// Gets the underlying numeric limit. From 3b2ecf44a0ee8437f8eb0e5b607312de6af7312a Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sun, 31 Jul 2022 19:27:34 +0200 Subject: [PATCH 23/28] Give variables more descriptive names --- crates/hir-def/src/nameres/collector.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index f394c541719f3..8a6bb929c3df7 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1055,7 +1055,7 @@ impl DefCollector<'_> { }; let mut res = ReachedFixedPoint::Yes; macros.retain(|directive| { - let resolver2 = |path| { + let resolver = |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -1068,7 +1068,7 @@ impl DefCollector<'_> { .take_macros() .map(|it| (it, macro_id_to_def_id(self.db, it))) }; - let resolver = |path| resolver2(path).map(|(_, it)| it); + let resolver_def_id = |path| resolver(path).map(|(_, it)| it); match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { @@ -1077,7 +1077,7 @@ impl DefCollector<'_> { ast_id, *expand_to, self.def_map.krate, - &resolver, + &resolver_def_id, &mut |_err| (), ); if let Ok(Ok(call_id)) = call_id { @@ -1093,7 +1093,7 @@ impl DefCollector<'_> { *derive_attr, *derive_pos as u32, self.def_map.krate, - &resolver2, + &resolver, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1158,7 +1158,7 @@ impl DefCollector<'_> { } } - let def = match resolver(path.clone()) { + let def = match resolver_def_id(path.clone()) { Some(def) if def.is_attribute() => def, _ => return true, }; @@ -1292,7 +1292,8 @@ impl DefCollector<'_> { true }); // Attribute resolution can add unresolved macro invocations, so concatenate the lists. - self.unresolved_macros.extend(macros); + macros.extend(mem::take(&mut self.unresolved_macros)); + self.unresolved_macros = macros; for (module_id, depth, container, macro_call_id) in resolved { self.collect_macro_expansion(module_id, macro_call_id, depth, container); From f2164c324cedfed8e655342b26b2831bdb39c5f2 Mon Sep 17 00:00:00 2001 From: Joshua Nelson <jnelson@cloudflare.com> Date: Sun, 31 Jul 2022 20:18:15 -0500 Subject: [PATCH 24/28] Update all pre-cloned submodules on startup Fixes https://github.com/rust-lang/rust/issues/99083 --- src/bootstrap/lib.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index cd421c249d8da..f84de73297acb 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -624,20 +624,6 @@ impl Build { /// If any submodule has been initialized already, sync it unconditionally. /// This avoids contributors checking in a submodule change by accident. pub fn maybe_update_submodules(&self) { - // WARNING: keep this in sync with the submodules hard-coded in bootstrap.py - let mut bootstrap_submodules: Vec<&str> = vec![ - "src/tools/rust-installer", - "src/tools/cargo", - "src/tools/rls", - "src/tools/miri", - "library/backtrace", - "library/stdarch", - ]; - // As in bootstrap.py, we include `rust-analyzer` if `build.vendor` was set in - // `config.toml`. - if self.config.vendor { - bootstrap_submodules.push("src/tools/rust-analyzer"); - } // Avoid running git when there isn't a git checkout. if !self.config.submodules(&self.rust_info) { return; @@ -653,10 +639,8 @@ impl Build { // Look for `submodule.$name.path = $path` // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer` let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap()); - // avoid updating submodules twice - if !bootstrap_submodules.iter().any(|&p| Path::new(p) == submodule) - && channel::GitInfo::new(false, submodule).is_git() - { + // Don't update the submodule unless it's already been cloned. + if channel::GitInfo::new(false, submodule).is_git() { self.update_submodule(submodule); } } From d0ea440dfe8659debd01246b5f2b8448abfb1e8b Mon Sep 17 00:00:00 2001 From: Preston From <prestonfrom@gmail.com> Date: Fri, 29 Jul 2022 22:52:46 -0600 Subject: [PATCH 25/28] Improve position named arguments lint underline and formatting names For named arguments used as implicit position arguments, underline both the opening curly brace and either: * if there is formatting, the next character (which will either be the closing curl brace or the `:` denoting the start of formatting args) * if there is no formatting, the entire arg span (important if there is whitespace like `{ }`) This should make it more obvious where the named argument should be. Additionally, in the lint message, emit the formatting argument names without a dollar sign to avoid potentially confusion. Fixes #99907 --- compiler/rustc_builtin_macros/src/format.rs | 86 +++++++--- compiler/rustc_lint/src/context.rs | 17 +- compiler/rustc_lint_defs/src/lib.rs | 14 +- src/test/ui/macros/issue-98466.stderr | 36 ++-- src/test/ui/macros/issue-99265.stderr | 172 ++++++++++---------- src/test/ui/macros/issue-99907.fixed | 24 +++ src/test/ui/macros/issue-99907.rs | 24 +++ src/test/ui/macros/issue-99907.stderr | 68 ++++++++ 8 files changed, 305 insertions(+), 136 deletions(-) create mode 100644 src/test/ui/macros/issue-99907.fixed create mode 100644 src/test/ui/macros/issue-99907.rs create mode 100644 src/test/ui/macros/issue-99907.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 082c78934262b..e6d514e76d2e5 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -16,6 +16,7 @@ use smallvec::SmallVec; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; +use rustc_parse_format::Count; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -57,26 +58,47 @@ struct PositionalNamedArg { replacement: Symbol, /// The span for the positional named argument (so the lint can point a message to it) positional_named_arg_span: Span, + has_formatting: bool, } impl PositionalNamedArg { - /// Determines what span to replace with the name of the named argument - fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> { + /// Determines: + /// 1) span to be replaced with the name of the named argument and + /// 2) span to be underlined for error messages + fn get_positional_arg_spans(&self, cx: &Context<'_, '_>) -> (Option<Span>, Option<Span>) { if let Some(inner_span) = &self.inner_span_to_replace { - return Some( - cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }), - ); + let span = + cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }); + (Some(span), Some(span)) } else if self.ty == PositionalNamedArgType::Arg { - // In the case of a named argument whose position is implicit, there will not be a span - // to replace. Instead, we insert the name after the `{`, which is the first character - // of arg_span. - return cx - .arg_spans - .get(self.cur_piece) - .map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()); + // In the case of a named argument whose position is implicit, if the argument *has* + // formatting, there will not be a span to replace. Instead, we insert the name after + // the `{`, which will be the first character of arg_span. If the argument does *not* + // have formatting, there may or may not be a span to replace. This is because + // whitespace is allowed in arguments without formatting (such as `format!("{ }", 1);`) + // but is not allowed in arguments with formatting (an error will be generated in cases + // like `format!("{ :1.1}", 1.0f32);`. + // For the message span, if there is formatting, we want to use the opening `{` and the + // next character, which will the `:` indicating the start of formatting. If there is + // not any formatting, we want to underline the entire span. + if self.has_formatting { + cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { + ( + Some(arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()), + Some(arg_span.with_hi(arg_span.lo() + BytePos(2))), + ) + }) + } else { + cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { + let replace_start = arg_span.lo() + BytePos(1); + let replace_end = arg_span.hi() - BytePos(1); + let to_replace = arg_span.with_lo(replace_start).with_hi(replace_end); + (Some(to_replace), Some(*arg_span)) + }) + } + } else { + (None, None) } - - None } } @@ -117,10 +139,18 @@ impl PositionalNamedArgsLint { cur_piece: usize, inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, names: &FxHashMap<Symbol, (usize, Span)>, + has_formatting: bool, ) { let start_of_named_args = total_args_length - names.len(); if current_positional_arg >= start_of_named_args { - self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names) + self.maybe_push( + format_argument_index, + ty, + cur_piece, + inner_span_to_replace, + names, + has_formatting, + ) } } @@ -134,6 +164,7 @@ impl PositionalNamedArgsLint { cur_piece: usize, inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, names: &FxHashMap<Symbol, (usize, Span)>, + has_formatting: bool, ) { let named_arg = names .iter() @@ -156,6 +187,7 @@ impl PositionalNamedArgsLint { inner_span_to_replace, replacement, positional_named_arg_span, + has_formatting, }); } } @@ -414,6 +446,9 @@ impl<'a, 'b> Context<'a, 'b> { PositionalNamedArgType::Precision, ); + let has_precision = arg.format.precision != Count::CountImplied; + let has_width = arg.format.width != Count::CountImplied; + // argument second, if it's an implicit positional parameter // it's written second, so it should come after width/precision. let pos = match arg.position { @@ -426,6 +461,7 @@ impl<'a, 'b> Context<'a, 'b> { self.curpiece, arg_end, &self.names, + has_precision || has_width, ); Exact(i) @@ -439,6 +475,7 @@ impl<'a, 'b> Context<'a, 'b> { self.curpiece, None, &self.names, + has_precision || has_width, ); Exact(i) } @@ -529,6 +566,7 @@ impl<'a, 'b> Context<'a, 'b> { self.curpiece, *inner_span, &self.names, + true, ); self.verify_arg_type(Exact(i), Count); } @@ -1150,24 +1188,22 @@ pub fn expand_format_args_nl<'cx>( fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) { for named_arg in &cx.unused_names_lint.positional_named_args { - let arg_span = named_arg.get_span_to_replace(cx); + let (position_sp_to_replace, position_sp_for_msg) = named_arg.get_positional_arg_spans(cx); let msg = format!("named argument `{}` is not used by name", named_arg.replacement); - let replacement = match named_arg.ty { - PositionalNamedArgType::Arg => named_arg.replacement.to_string(), - _ => named_arg.replacement.to_string() + "$", - }; cx.ecx.buffered_early_lint.push(BufferedEarlyLint { span: MultiSpan::from_span(named_arg.positional_named_arg_span), msg: msg.clone(), node_id: ast::CRATE_NODE_ID, lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), - diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally( - arg_span, - named_arg.positional_named_arg_span, - replacement, - ), + diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally { + position_sp_to_replace, + position_sp_for_msg, + named_arg_sp: named_arg.positional_named_arg_span, + named_arg_name: named_arg.replacement.to_string(), + is_formatting_arg: named_arg.ty != PositionalNamedArgType::Arg, + }, }); } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 04ac50f1d4806..b95fc341db656 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -856,13 +856,18 @@ pub trait LintContext: Sized { Applicability::MachineApplicable, ); }, - BuiltinLintDiagnostics::NamedArgumentUsedPositionally(positional_arg, named_arg, name) => { - db.span_label(named_arg, "this named argument is only referred to by position in formatting string"); - if let Some(positional_arg) = positional_arg { - let msg = format!("this formatting argument uses named argument `{}` by position", name); - db.span_label(positional_arg, msg); + BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => { + db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string"); + if let Some(positional_arg_for_msg) = position_sp_for_msg { + let msg = format!("this formatting argument uses named argument `{}` by position", named_arg_name); + db.span_label(positional_arg_for_msg, msg); + } + + if let Some(positional_arg_to_replace) = position_sp_to_replace { + let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name }; + db.span_suggestion_verbose( - positional_arg, + positional_arg_to_replace, "use the named argument by name to avoid ambiguity", name, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4fd57ed853379..6acbe97a7a118 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -467,7 +467,19 @@ pub enum BuiltinLintDiagnostics { /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, }, - NamedArgumentUsedPositionally(Option<Span>, Span, String), + NamedArgumentUsedPositionally { + /// Span where the named argument is used by position and will be replaced with the named + /// argument name + position_sp_to_replace: Option<Span>, + /// Span where the named argument is used by position and is used for lint messages + position_sp_for_msg: Option<Span>, + /// Span where the named argument's name is (so we know where to put the warning message) + named_arg_sp: Span, + /// String containing the named arguments name + named_arg_name: String, + /// Indicates if the named argument is used as a width/precision for formatting + is_formatting_arg: bool, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/src/test/ui/macros/issue-98466.stderr b/src/test/ui/macros/issue-98466.stderr index 4a39dd1440b1f..c93451c761ae4 100644 --- a/src/test/ui/macros/issue-98466.stderr +++ b/src/test/ui/macros/issue-98466.stderr @@ -2,9 +2,9 @@ warning: named argument `_x` is not used by name --> $DIR/issue-98466.rs:7:26 | LL | println!("_x is {}", _x = 5); - | - ^^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `_x` by position + | -- ^^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `_x` by position | = note: `#[warn(named_arguments_used_positionally)]` on by default help: use the named argument by name to avoid ambiguity @@ -16,9 +16,9 @@ warning: named argument `y` is not used by name --> $DIR/issue-98466.rs:10:26 | LL | println!("_x is {}", y = _x); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `y` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position | help: use the named argument by name to avoid ambiguity | @@ -29,9 +29,9 @@ warning: named argument `y` is not used by name --> $DIR/issue-98466.rs:13:83 | LL | println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `y` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position | help: use the named argument by name to avoid ambiguity | @@ -42,9 +42,9 @@ warning: named argument `_x` is not used by name --> $DIR/issue-98466.rs:19:34 | LL | let _f = format!("_x is {}", _x = 5); - | - ^^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `_x` by position + | -- ^^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `_x` by position | help: use the named argument by name to avoid ambiguity | @@ -55,9 +55,9 @@ warning: named argument `y` is not used by name --> $DIR/issue-98466.rs:22:34 | LL | let _f = format!("_x is {}", y = _x); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `y` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position | help: use the named argument by name to avoid ambiguity | @@ -68,9 +68,9 @@ warning: named argument `y` is not used by name --> $DIR/issue-98466.rs:25:91 | LL | let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `y` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position | help: use the named argument by name to avoid ambiguity | diff --git a/src/test/ui/macros/issue-99265.stderr b/src/test/ui/macros/issue-99265.stderr index 0798ad8dc517c..2bfeedd7d0737 100644 --- a/src/test/ui/macros/issue-99265.stderr +++ b/src/test/ui/macros/issue-99265.stderr @@ -2,9 +2,9 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:5:24 | LL | println!("{b} {}", a=1, b=2); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `a` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `a` by position | = note: `#[warn(named_arguments_used_positionally)]` on by default help: use the named argument by name to avoid ambiguity @@ -16,9 +16,9 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:9:35 | LL | println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `a` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `a` by position | help: use the named argument by name to avoid ambiguity | @@ -29,9 +29,9 @@ warning: named argument `b` is not used by name --> $DIR/issue-99265.rs:9:40 | LL | println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `b` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `b` by position | help: use the named argument by name to avoid ambiguity | @@ -42,9 +42,9 @@ warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:9:45 | LL | println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `c` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `c` by position | help: use the named argument by name to avoid ambiguity | @@ -55,9 +55,9 @@ warning: named argument `d` is not used by name --> $DIR/issue-99265.rs:9:50 | LL | println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `d` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `d` by position | help: use the named argument by name to avoid ambiguity | @@ -68,9 +68,9 @@ warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:19:35 | LL | println!("Hello {:1$}!", "x", width = 5); - | -- ^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `width$` by position + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | @@ -81,9 +81,9 @@ warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:23:46 | LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `width$` by position + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | @@ -94,9 +94,9 @@ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:23:57 | LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `precision$` by position + | this formatting argument uses named argument `precision` by position | help: use the named argument by name to avoid ambiguity | @@ -107,9 +107,9 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:23:33 | LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `f` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position | help: use the named argument by name to avoid ambiguity | @@ -120,9 +120,9 @@ warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:31:47 | LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `width$` by position + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | @@ -133,9 +133,9 @@ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:31:58 | LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `precision$` by position + | this formatting argument uses named argument `precision` by position | help: use the named argument by name to avoid ambiguity | @@ -146,7 +146,7 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:31:34 | LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | - ^ this named argument is only referred to by position in formatting string + | - ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `f` by position | @@ -159,10 +159,10 @@ warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:52:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `width$` by position + | -- this formatting argument uses named argument `width` by position ... LL | width = 5, - | ^^^^^ this named argument is only referred to by position in formatting string + | ^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -173,10 +173,10 @@ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:54:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `precision$` by position + | -- this formatting argument uses named argument `precision` by position ... LL | precision = 2, - | ^^^^^^^^^ this named argument is only referred to by position in formatting string + | ^^^^^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -190,7 +190,7 @@ LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", | - this formatting argument uses named argument `f` by position ... LL | f = 0.02f32, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -201,10 +201,10 @@ warning: named argument `width2` is not used by name --> $DIR/issue-99265.rs:58:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `width2$` by position + | -- this formatting argument uses named argument `width2` by position ... LL | width2 = 5, - | ^^^^^^ this named argument is only referred to by position in formatting string + | ^^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -215,10 +215,10 @@ warning: named argument `precision2` is not used by name --> $DIR/issue-99265.rs:60:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `precision2$` by position + | -- this formatting argument uses named argument `precision2` by position ... LL | precision2 = 2 - | ^^^^^^^^^^ this named argument is only referred to by position in formatting string + | ^^^^^^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -232,7 +232,7 @@ LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", | - this formatting argument uses named argument `g` by position ... LL | g = 0.02f32, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -246,7 +246,7 @@ LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", | - this formatting argument uses named argument `f` by position ... LL | f = 0.02f32, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -257,9 +257,9 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:64:31 | LL | println!("Hello {:0.1}!", f = 0.02f32); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `f` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position | help: use the named argument by name to avoid ambiguity | @@ -270,7 +270,7 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:68:32 | LL | println!("Hello {0:0.1}!", f = 0.02f32); - | - ^ this named argument is only referred to by position in formatting string + | - ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `f` by position | @@ -283,9 +283,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:79:23 | LL | println!("{:0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -296,9 +296,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:79:23 | LL | println!("{:0$}", v = val); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -309,9 +309,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:84:24 | LL | println!("{0:0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -322,7 +322,7 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:84:24 | LL | println!("{0:0$}", v = val); - | - ^ this named argument is only referred to by position in formatting string + | - ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `v` by position | @@ -335,9 +335,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 | LL | println!("{:0$.0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -348,9 +348,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 | LL | println!("{:0$.0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -361,9 +361,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 | LL | println!("{:0$.0$}", v = val); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -374,9 +374,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 | LL | println!("{0:0$.0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -387,9 +387,9 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 | LL | println!("{0:0$.0$}", v = val); - | -- ^ this named argument is only referred to by position in formatting string + | -- ^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `v$` by position + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | @@ -400,7 +400,7 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 | LL | println!("{0:0$.0$}", v = val); - | - ^ this named argument is only referred to by position in formatting string + | - ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `v` by position | @@ -413,9 +413,9 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:104:28 | LL | println!("{} {a} {0}", a = 1); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `a` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `a` by position | help: use the named argument by name to avoid ambiguity | @@ -426,7 +426,7 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:104:28 | LL | println!("{} {a} {0}", a = 1); - | - ^ this named argument is only referred to by position in formatting string + | - ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `a` by position | @@ -439,10 +439,10 @@ warning: named argument `b` is not used by name --> $DIR/issue-99265.rs:115:23 | LL | {:1$.2$}", - | -- this formatting argument uses named argument `b$` by position + | -- this formatting argument uses named argument `b` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -453,10 +453,10 @@ warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:115:30 | LL | {:1$.2$}", - | -- this formatting argument uses named argument `c$` by position + | -- this formatting argument uses named argument `c` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -467,10 +467,10 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:115:14 | LL | {:1$.2$}", - | - this formatting argument uses named argument `a` by position + | -- this formatting argument uses named argument `a` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -481,10 +481,10 @@ warning: named argument `b` is not used by name --> $DIR/issue-99265.rs:126:23 | LL | {0:1$.2$}", - | -- this formatting argument uses named argument `b$` by position + | -- this formatting argument uses named argument `b` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -495,10 +495,10 @@ warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:126:30 | LL | {0:1$.2$}", - | -- this formatting argument uses named argument `c$` by position + | -- this formatting argument uses named argument `c` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -512,7 +512,7 @@ LL | {0:1$.2$}", | - this formatting argument uses named argument `a` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is only referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | @@ -523,9 +523,9 @@ warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:132:39 | LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | -- ^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `width$` by position + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | @@ -536,9 +536,9 @@ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:132:50 | LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | -- ^^^^^^^^^ this named argument is only referred to by position in formatting string + | -- ^^^^^^^^^ this named argument is referred to by position in formatting string | | - | this formatting argument uses named argument `precision$` by position + | this formatting argument uses named argument `precision` by position | help: use the named argument by name to avoid ambiguity | @@ -549,9 +549,9 @@ warning: named argument `x` is not used by name --> $DIR/issue-99265.rs:132:30 | LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | - ^ this named argument is only referred to by position in formatting string - | | - | this formatting argument uses named argument `x` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `x` by position | help: use the named argument by name to avoid ambiguity | diff --git a/src/test/ui/macros/issue-99907.fixed b/src/test/ui/macros/issue-99907.fixed new file mode 100644 index 0000000000000..9e0e1b80ee59f --- /dev/null +++ b/src/test/ui/macros/issue-99907.fixed @@ -0,0 +1,24 @@ +// check-pass +// run-rustfix + +fn main() { + println!("Hello {f:.1}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {f:1.1}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {f}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {f}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {f}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity +} diff --git a/src/test/ui/macros/issue-99907.rs b/src/test/ui/macros/issue-99907.rs new file mode 100644 index 0000000000000..eebcfc2efecc9 --- /dev/null +++ b/src/test/ui/macros/issue-99907.rs @@ -0,0 +1,24 @@ +// check-pass +// run-rustfix + +fn main() { + println!("Hello {:.1}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {:1.1}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello {}!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello { }!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + println!("Hello { }!", f = 0.02f32); + //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity +} diff --git a/src/test/ui/macros/issue-99907.stderr b/src/test/ui/macros/issue-99907.stderr new file mode 100644 index 0000000000000..4786ce003b4c2 --- /dev/null +++ b/src/test/ui/macros/issue-99907.stderr @@ -0,0 +1,68 @@ +warning: named argument `f` is not used by name + --> $DIR/issue-99907.rs:5:30 + | +LL | println!("Hello {:.1}!", f = 0.02f32); + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position + | + = note: `#[warn(named_arguments_used_positionally)]` on by default +help: use the named argument by name to avoid ambiguity + | +LL | println!("Hello {f:.1}!", f = 0.02f32); + | + + +warning: named argument `f` is not used by name + --> $DIR/issue-99907.rs:9:31 + | +LL | println!("Hello {:1.1}!", f = 0.02f32); + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("Hello {f:1.1}!", f = 0.02f32); + | + + +warning: named argument `f` is not used by name + --> $DIR/issue-99907.rs:13:27 + | +LL | println!("Hello {}!", f = 0.02f32); + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("Hello {f}!", f = 0.02f32); + | + + +warning: named argument `f` is not used by name + --> $DIR/issue-99907.rs:17:28 + | +LL | println!("Hello { }!", f = 0.02f32); + | --- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("Hello {f}!", f = 0.02f32); + | + + +warning: named argument `f` is not used by name + --> $DIR/issue-99907.rs:21:29 + | +LL | println!("Hello { }!", f = 0.02f32); + | ---- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("Hello {f}!", f = 0.02f32); + | + + +warning: 5 warnings emitted + From 298acef30730ee676fdbc9e731370cd7ccedd431 Mon Sep 17 00:00:00 2001 From: Preston From <prestonfrom@gmail.com> Date: Tue, 2 Aug 2022 00:20:12 -0600 Subject: [PATCH 26/28] Move if-block into closure to reduce duplicate code --- compiler/rustc_builtin_macros/src/format.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e6d514e76d2e5..4f3cda16f037d 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -81,21 +81,19 @@ impl PositionalNamedArg { // For the message span, if there is formatting, we want to use the opening `{` and the // next character, which will the `:` indicating the start of formatting. If there is // not any formatting, we want to underline the entire span. - if self.has_formatting { - cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { + cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { + if self.has_formatting { ( Some(arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()), Some(arg_span.with_hi(arg_span.lo() + BytePos(2))), ) - }) - } else { - cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { + } else { let replace_start = arg_span.lo() + BytePos(1); let replace_end = arg_span.hi() - BytePos(1); let to_replace = arg_span.with_lo(replace_start).with_hi(replace_end); (Some(to_replace), Some(*arg_span)) - }) - } + } + }) } else { (None, None) } From 63e04eeb6d9a92084d283c57709dc1cc5031995e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi <mqudsi@neosmart.net> Date: Tue, 2 Aug 2022 12:50:06 -0500 Subject: [PATCH 27/28] Clarify Cargo.toml comments Existing comments were left in an incoherent state after #99768. --- src/tools/rustc-workspace-hack/Cargo.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 00281bf8f014a..b1d8b86496ad0 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -79,11 +79,10 @@ crossbeam-utils = { version = "0.8.0", features = ["nightly"] } libc = { version = "0.2.79", features = ["align"] } # Ensure default features of libz-sys, which are disabled in some scenarios. libz-sys = { version = "1.1.2" } - -# looks like the only user of deprecated `use_std` feature is `combine`, so this -# can be removed if/when https://github.com/Marwes/combine/pull/348 be merged and released. +# The only user of memchr's deprecated `use_std` feature is `combine`, so this can be +# removed if/when https://github.com/Marwes/combine/pull/348 is merged and released. memchr = { version = "2.5", features = ["std", "use_std"] } -# same for regex +# Ensure default features of regex, which are disabled in some scenarios. regex = { version = "1.5.6" } proc-macro2 = { version = "1", features = ["default"] } quote = { version = "1", features = ["default"] } From 2bbdc4158e57c7839f381bff32ff294106e8e66b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com> Date: Tue, 2 Aug 2022 22:29:29 +0300 Subject: [PATCH 28/28] rustc-docs: Be less specific about the representation of `+bundle` --- src/doc/rustc/src/command-line-arguments.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index bc04dfd4433f6..f05ff3f1b6b4e 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -89,9 +89,9 @@ but it is not guaranteed. If you need whole archive semantics use `+whole-archiv This modifier is only compatible with the `static` linking kind. Using any other kind will result in a compiler error. -When building a rlib or staticlib `+bundle` means that all object files from the native static -library will be added to the rlib or staticlib archive, and then used from it during linking of -the final binary. +When building a rlib or staticlib `+bundle` means that the native static library +will be packed into the rlib or staticlib archive, and then retrieved from there +during linking of the final binary. When building a rlib `-bundle` means that the native static library is registered as a dependency of that rlib "by name", and object files from it are included only during linking of the final