From 3e4de963a2f932a4591b037e6b5933ac5903b7e3 Mon Sep 17 00:00:00 2001 From: jprochazk <1665677+jprochazk@users.noreply.github.com> Date: Sat, 28 Oct 2023 16:34:00 +0200 Subject: [PATCH 01/31] override language client --- editors/code/src/base_client.ts | 12 ++++++++++++ editors/code/src/client.ts | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 editors/code/src/base_client.ts diff --git a/editors/code/src/base_client.ts b/editors/code/src/base_client.ts new file mode 100644 index 0000000000000..085920fb66039 --- /dev/null +++ b/editors/code/src/base_client.ts @@ -0,0 +1,12 @@ +import * as lc from "vscode-languageclient/node"; + +export class RaLanguageClient extends lc.LanguageClient { + override error(message: string, data?: any, showNotification?: boolean | "force"): void { + // ignore `Request TYPE failed.` errors + if (message.startsWith("Request") && message.endsWith("failed.")) { + return; + } + + super.error(message, data, showNotification); + } +} diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 96e888402baf2..ed7066a1b7bab 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -10,6 +10,7 @@ import { type Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; import { sep as pathSeparator } from "path"; import { unwrapUndefinable } from "./undefinable"; +import { RaLanguageClient } from "./base_client"; export interface Env { [name: string]: string; @@ -363,7 +364,7 @@ export async function createClient( }, }; - const client = new lc.LanguageClient( + const client = new RaLanguageClient( "rust-analyzer", "Rust Analyzer Language Server", serverOptions, From c566136854734ff32ddaaed107c74759a3a3862f Mon Sep 17 00:00:00 2001 From: jprochazk <1665677+jprochazk@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:33:45 +0100 Subject: [PATCH 02/31] add configuration option --- editors/code/package.json | 5 +++++ editors/code/src/client.ts | 2 +- editors/code/src/{base_client.ts => lang_client.ts} | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) rename editors/code/src/{base_client.ts => lang_client.ts} (56%) diff --git a/editors/code/package.json b/editors/code/package.json index 2dde66c97006b..59f89cebdeaa0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -502,6 +502,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.showRequestFailedErrorNotification": { + "markdownDescription": "Whether to show panic error notifications.", + "default": true, + "type": "boolean" + }, "rust-analyzer.showDependenciesExplorer": { "markdownDescription": "Whether to show the dependencies view.", "default": true, diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index ed7066a1b7bab..c27a446b38043 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -10,7 +10,7 @@ import { type Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; import { sep as pathSeparator } from "path"; import { unwrapUndefinable } from "./undefinable"; -import { RaLanguageClient } from "./base_client"; +import { RaLanguageClient } from "./lang_client"; export interface Env { [name: string]: string; diff --git a/editors/code/src/base_client.ts b/editors/code/src/lang_client.ts similarity index 56% rename from editors/code/src/base_client.ts rename to editors/code/src/lang_client.ts index 085920fb66039..e28330e6dba61 100644 --- a/editors/code/src/base_client.ts +++ b/editors/code/src/lang_client.ts @@ -1,9 +1,13 @@ import * as lc from "vscode-languageclient/node"; +import * as vscode from "vscode"; export class RaLanguageClient extends lc.LanguageClient { override error(message: string, data?: any, showNotification?: boolean | "force"): void { // ignore `Request TYPE failed.` errors - if (message.startsWith("Request") && message.endsWith("failed.")) { + const showError = vscode.workspace + .getConfiguration("rust-analyzer") + .get("showRequestFailedErrorNotification"); + if (!showError && message.startsWith("Request") && message.endsWith("failed.")) { return; } From 0647b645cd7bc83686065796f9cd8f3b44448711 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Fri, 10 Nov 2023 17:39:32 +0100 Subject: [PATCH 03/31] ensure renames happen after edit --- crates/rust-analyzer/src/lsp/to_proto.rs | 23 ++++++++++++---- crates/rust-analyzer/tests/slow-tests/main.rs | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index aca91570f7c27..4e3dce35ec396 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1,7 +1,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. use std::{ iter::once, - path, + mem, path, sync::atomic::{AtomicU32, Ordering}, }; @@ -1123,13 +1123,20 @@ pub(crate) fn snippet_text_document_ops( pub(crate) fn snippet_workspace_edit( snap: &GlobalStateSnapshot, - source_change: SourceChange, + mut source_change: SourceChange, ) -> Cancellable { let mut document_changes: Vec = Vec::new(); - for op in source_change.file_system_edits { - let ops = snippet_text_document_ops(snap, op)?; - document_changes.extend_from_slice(&ops); + for op in &mut source_change.file_system_edits { + if let FileSystemEdit::CreateFile { dst, initial_contents } = op { + // replace with a placeholder to avoid cloneing the edit + let op = FileSystemEdit::CreateFile { + dst: dst.clone(), + initial_contents: mem::take(initial_contents), + }; + let ops = snippet_text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } } for (file_id, (edit, snippet_edit)) in source_change.source_file_edits { let edit = snippet_text_document_edit( @@ -1141,6 +1148,12 @@ pub(crate) fn snippet_workspace_edit( )?; document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); } + for op in source_change.file_system_edits { + if !matches!(op, FileSystemEdit::CreateFile { .. }) { + let ops = snippet_text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } + } let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes), diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index d59914298991c..5cd02f7840460 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -984,6 +984,11 @@ fn main() {} //- /src/old_file.rs //- /src/old_folder/mod.rs +mod nested; + +//- /src/old_folder/nested.rs +struct foo; +use crate::old_folder::nested::foo as bar; //- /src/from_mod/mod.rs @@ -1080,6 +1085,27 @@ fn main() {} "newText": "new_folder" } ] + }, + { + "textDocument": { + "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace('\\', "/")), + "version": null + }, + "edits": [ + { + "range": { + "start": { + "line": 1, + "character": 11 + }, + "end": { + "line": 1, + "character": 21 + } + }, + "newText": "new_folder" + } + ] } ] }), From 67058605d82f645582ebc3a96186efe50eeb9593 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 13 Nov 2023 13:13:39 +0100 Subject: [PATCH 04/31] FIxed typo in highlight_related.rs --- crates/ide/src/highlight_related.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 46a0464e9e6ea..a7f5ae92a4cac 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -43,7 +43,7 @@ pub struct HighlightRelatedConfig { // // . if on an identifier, highlights all references to that identifier in the current file // .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope -// . if on an `async` or `await token, highlights all yield points for that async context +// . if on an `async` or `await` token, highlights all yield points for that async context // . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context // . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context // . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. From 0d147b382f46d9f19145d03295519ca4558858af Mon Sep 17 00:00:00 2001 From: jprochazk <1665677+jprochazk@users.noreply.github.com> Date: Wed, 15 Nov 2023 12:36:08 +0100 Subject: [PATCH 05/31] detect internal error via `error.code` instead of error message --- editors/code/src/lang_client.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/editors/code/src/lang_client.ts b/editors/code/src/lang_client.ts index e28330e6dba61..09d64efc048ae 100644 --- a/editors/code/src/lang_client.ts +++ b/editors/code/src/lang_client.ts @@ -2,15 +2,25 @@ import * as lc from "vscode-languageclient/node"; import * as vscode from "vscode"; export class RaLanguageClient extends lc.LanguageClient { - override error(message: string, data?: any, showNotification?: boolean | "force"): void { - // ignore `Request TYPE failed.` errors + override handleFailedRequest( + type: lc.MessageSignature, + token: vscode.CancellationToken | undefined, + error: any, + defaultValue: T, + showNotification?: boolean | undefined, + ): T { const showError = vscode.workspace .getConfiguration("rust-analyzer") .get("showRequestFailedErrorNotification"); - if (!showError && message.startsWith("Request") && message.endsWith("failed.")) { - return; + if ( + !showError && + error instanceof lc.ResponseError && + error.code === lc.ErrorCodes.InternalError + ) { + // Don't show notification for internal errors, these are emitted by r-a when a request fails. + showNotification = false; } - super.error(message, data, showNotification); + return super.handleFailedRequest(type, token, error, defaultValue, showNotification); } } From 9c8727eea5ba01c87cf384b1864e7ce86f31111d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <1665677+jprochazk@users.noreply.github.com> Date: Wed, 15 Nov 2023 12:41:54 +0100 Subject: [PATCH 06/31] Update editors/code/package.json Co-authored-by: Lukas Wirth --- editors/code/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/package.json b/editors/code/package.json index 59f89cebdeaa0..850c0772f3814 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -503,7 +503,7 @@ "type": "boolean" }, "rust-analyzer.showRequestFailedErrorNotification": { - "markdownDescription": "Whether to show panic error notifications.", + "markdownDescription": "Whether to show error notifications for failing requests.", "default": true, "type": "boolean" }, From cfc881ae137af972a2240df9c92492abd314bbb8 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Thu, 16 Nov 2023 22:27:21 +0800 Subject: [PATCH 07/31] fix missing assoc item --- crates/hir/src/lib.rs | 10 +++++----- .../src/handlers/trait_impl_missing_assoc_item.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bdc11aa3561b7..1bfbf7212bf0a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -667,21 +667,21 @@ impl Module { let items = &db.trait_data(trait_.into()).items; let required_items = items.iter().filter(|&(_, assoc)| match *assoc { AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), - AssocItemId::ConstId(_) => true, + AssocItemId::ConstId(id) => Const::from(id).value(db).is_none(), AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), }); - impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().map( + impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map( |&item| { - ( + Some(( item, match item { AssocItemId::FunctionId(it) => db.function_data(it).name.clone(), AssocItemId::ConstId(it) => { - db.const_data(it).name.as_ref().unwrap().clone() + db.const_data(it).name.as_ref()?.clone() } AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(), }, - ) + )) }, )); diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 40d0b6fdd4b0e..51923797ac91a 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -35,6 +35,19 @@ pub(crate) fn trait_impl_missing_assoc_item( mod tests { use crate::tests::check_diagnostics; + #[test] + fn trait_with_default_value() { + check_diagnostics( + r#" +trait Marker { + const FLAG: bool = false; +} +struct Foo; +impl Marker for Foo {} + "#, + ) + } + #[test] fn simple() { check_diagnostics( From e95ec552731be34a0820ded3c001a91398b6a2e1 Mon Sep 17 00:00:00 2001 From: austaras Date: Thu, 2 Nov 2023 22:37:50 +0800 Subject: [PATCH 08/31] fix: better resolve assoc item with type bound --- crates/hir-ty/src/infer/unify.rs | 29 ++++++++ crates/hir-ty/src/lower.rs | 23 +++++- crates/hir-ty/src/method_resolution.rs | 50 ++++++++----- crates/ide-completion/src/completions/dot.rs | 77 ++++++++++++++++++++ 4 files changed, 156 insertions(+), 23 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 0a68a9f3b5831..78bdd62a2769f 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -495,6 +495,35 @@ impl<'a> InferenceTable<'a> { solution } + pub(crate) fn try_resolve_alias(&mut self, goal: Goal) -> bool { + let in_env = InEnvironment::new(&self.trait_env.env, goal); + let canonicalized = self.canonicalize(in_env); + let solution = self.db.trait_solve( + self.trait_env.krate, + self.trait_env.block, + canonicalized.value.clone(), + ); + + match solution { + Some(Solution::Unique(canonical_subst)) => { + canonicalized.apply_solution( + self, + Canonical { + binders: canonical_subst.binders, + value: canonical_subst.value.subst, + }, + ); + true + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(self, substs); + true + } + Some(_) => true, + None => false, + } + } + pub(crate) fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index c8a85b4a9ff09..04005311b673b 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1097,10 +1097,25 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - let ty = self.lower_ty(type_ref); - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = + (type_ref, &self.impl_trait_mode) + { + for bound in bounds { + predicates.extend( + self.lower_type_bound( + bound, + TyKind::Alias(AliasTy::Projection(projection_ty.clone())) + .intern(Interner), + false, + ), + ); + } + } else { + let ty = self.lower_ty(type_ref); + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + } } for bound in binding.bounds.iter() { predicates.extend(self.lower_type_bound( diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 87c93283361fc..04a55c3536dff 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1478,26 +1478,38 @@ fn is_valid_fn_candidate( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. let predicates = db.generic_predicates(impl_id.into()); - let valid = predicates - .iter() - .map(|predicate| { - let (p, b) = predicate - .clone() - .substitute(Interner, &impl_subst) - // Skipping the inner binders is ok, as we don't handle quantified where - // clauses yet. - .into_value_and_skipped_binders(); - stdx::always!(b.len(Interner) == 0); - p - }) - // It's ok to get ambiguity here, as we may not have enough information to prove - // obligations. We'll check if the user is calling the selected method properly - // later anyway. - .all(|p| table.try_obligation(p.cast(Interner)).is_some()); - match valid { - true => IsValidCandidate::Yes, - false => IsValidCandidate::No, + let mut alias = Vec::new(); + let mut other_predicate = Vec::new(); + + for predicate in predicates.iter() { + let (p, b) = predicate + .clone() + .substitute(Interner, &impl_subst) + // Skipping the inner binders is ok, as we don't handle quantified where + // clauses yet. + .into_value_and_skipped_binders(); + stdx::always!(b.len(Interner) == 0); + + if let WhereClause::AliasEq(_) = p { + alias.push(p); + } else { + other_predicate.push(p); + } + } + + for p in alias { + if !table.try_resolve_alias(p.cast(Interner)) { + return IsValidCandidate::No; + } } + + for p in other_predicate { + if table.try_obligation(p.cast(Interner)).is_none() { + return IsValidCandidate::No; + } + } + + IsValidCandidate::Yes } else { // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // `iterate_trait_method_candidates()`. diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index c5bbb7f8d755d..5bcc867fe1810 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -1095,4 +1095,81 @@ fn test(s: S) { "#]], ); } + + #[test] + fn assoc_impl_1() { + check( + r#" +//- minicore: deref +fn main() { + let foo: Foo<&u8> = Foo::new(&42_u8); + foo.$0 +} + +trait Bar { + fn bar(&self); +} + +impl Bar for u8 { + fn bar(&self) {} +} + +struct Foo { + foo: F, +} + +impl Foo { + fn new(foo: F) -> Foo { + Foo { foo } + } +} + +impl> Foo { + fn foobar(&self) { + self.foo.deref().bar() + } +} +"#, + expect![[r#" + fd foo &u8 + me foobar() fn(&self) + "#]], + ); + } + + #[test] + fn assoc_impl_2() { + check( + r#" +//- minicore: deref +fn main() { + let foo: Foo<&u8> = Foo::new(&42_u8); + foo.$0 +} + +trait Bar { + fn bar(&self); +} + +struct Foo { + foo: F, +} + +impl Foo { + fn new(foo: F) -> Foo { + Foo { foo } + } +} + +impl> Foo { + fn foobar(&self) { + self.foo.deref().bar() + } +} +"#, + expect![[r#" + fd foo &u8 + "#]], + ); + } } From 59f5d518527a6584062e278179f4b2e31f9243ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 16 Nov 2023 22:27:35 +0200 Subject: [PATCH 09/31] Merge commit '141fc695dca1df7cfc3c9803972ec19bb178dcbc' into sync-from-ra --- .github/workflows/release.yaml | 5 +- Cargo.lock | 268 ++++++++---------- Cargo.toml | 23 +- crates/cfg/Cargo.toml | 4 +- crates/flycheck/Cargo.toml | 4 +- crates/hir-def/Cargo.toml | 10 +- crates/hir-def/src/body/pretty.rs | 2 +- crates/hir-def/src/data.rs | 17 +- crates/hir-def/src/find_path.rs | 173 ++++++++--- crates/hir-def/src/generics.rs | 2 +- crates/hir-def/src/import_map.rs | 232 ++++++++------- crates/hir-def/src/item_tree.rs | 1 + crates/hir-def/src/item_tree/lower.rs | 17 +- crates/hir-def/src/item_tree/pretty.rs | 14 +- crates/hir-def/src/lib.rs | 2 +- .../macro_expansion_tests/builtin_fn_macro.rs | 4 +- .../macro_expansion_tests/mbe/regression.rs | 34 +++ crates/hir-expand/Cargo.toml | 6 +- crates/hir-expand/src/builtin_fn_macro.rs | 27 +- crates/hir-expand/src/db.rs | 29 +- crates/hir-ty/Cargo.toml | 24 +- crates/hir-ty/src/chalk_ext.rs | 5 + crates/hir-ty/src/db.rs | 26 +- crates/hir-ty/src/diagnostics.rs | 6 - crates/hir-ty/src/diagnostics/decl_check.rs | 28 +- crates/hir-ty/src/display.rs | 1 + crates/hir-ty/src/infer/closure.rs | 27 ++ crates/hir-ty/src/layout.rs | 57 ++-- crates/hir-ty/src/layout/adt.rs | 8 +- crates/hir-ty/src/layout/tests.rs | 7 +- crates/hir-ty/src/lib.rs | 1 + crates/hir-ty/src/lower.rs | 84 +++--- crates/hir-ty/src/method_resolution.rs | 56 ++++ crates/hir-ty/src/tests/macros.rs | 6 +- crates/hir-ty/src/tests/traits.rs | 56 ++-- crates/hir-ty/src/traits.rs | 4 +- crates/hir/Cargo.toml | 4 +- crates/hir/src/diagnostics.rs | 32 ++- crates/hir/src/display.rs | 61 +++- crates/hir/src/lib.rs | 146 ++++++++-- crates/ide-assists/Cargo.toml | 4 +- crates/ide-assists/src/assist_config.rs | 1 + .../src/handlers/add_missing_impl_members.rs | 31 ++ .../src/handlers/add_missing_match_arms.rs | 33 ++- .../src/handlers/add_turbo_fish.rs | 140 ++++++--- .../ide-assists/src/handlers/auto_import.rs | 1 + .../ide-assists/src/handlers/bool_to_enum.rs | 1 + .../src/handlers/convert_into_to_from.rs | 7 +- .../convert_tuple_return_type_to_struct.rs | 1 + .../src/handlers/destructure_tuple_binding.rs | 241 ++++++++++------ .../src/handlers/extract_function.rs | 1 + .../extract_struct_from_enum_variant.rs | 1 + .../src/handlers/generate_deref.rs | 16 +- .../ide-assists/src/handlers/generate_new.rs | 1 + .../src/handlers/qualify_method_call.rs | 1 + .../ide-assists/src/handlers/qualify_path.rs | 7 +- .../replace_derive_with_manual_impl.rs | 7 +- .../replace_qualified_name_with_use.rs | 1 + crates/ide-assists/src/tests.rs | 7 + crates/ide-assists/src/utils.rs | 14 +- crates/ide-completion/Cargo.toml | 2 +- crates/ide-completion/src/completions.rs | 1 + crates/ide-completion/src/completions/expr.rs | 2 + .../src/completions/flyimport.rs | 21 +- crates/ide-completion/src/config.rs | 1 + crates/ide-completion/src/lib.rs | 1 + crates/ide-completion/src/snippet.rs | 1 + crates/ide-completion/src/tests.rs | 1 + crates/ide-db/Cargo.toml | 14 +- crates/ide-db/src/imports/import_assets.rs | 19 +- crates/ide-db/src/path_transform.rs | 9 +- crates/ide-db/src/search.rs | 2 +- crates/ide-db/src/source_change.rs | 6 +- crates/ide-diagnostics/Cargo.toml | 6 +- .../src/handlers/incorrect_case.rs | 8 +- .../src/handlers/json_is_not_rust.rs | 2 + .../src/handlers/missing_fields.rs | 1 + .../src/handlers/mutability_errors.rs | 16 ++ .../handlers/trait_impl_incorrect_safety.rs | 129 +++++++++ .../handlers/trait_impl_missing_assoc_item.rs | 102 +++++++ .../src/handlers/trait_impl_orphan.rs | 106 +++++++ .../src/handlers/type_mismatch.rs | 17 ++ crates/ide-diagnostics/src/lib.rs | 8 + crates/ide-diagnostics/src/tests.rs | 18 +- crates/ide-ssr/Cargo.toml | 2 +- crates/ide-ssr/src/matching.rs | 2 +- crates/ide/Cargo.toml | 6 +- crates/ide/src/expand_macro.rs | 4 +- crates/ide/src/hover/tests.rs | 61 ++-- crates/ide/src/inlay_hints.rs | 17 ++ crates/ide/src/inlay_hints/chaining.rs | 20 +- crates/ide/src/references.rs | 26 ++ crates/load-cargo/Cargo.toml | 8 +- crates/load-cargo/src/lib.rs | 1 + crates/mbe/Cargo.toml | 2 +- crates/parser/src/event.rs | 5 - crates/proc-macro-api/Cargo.toml | 2 +- crates/proc-macro-test/Cargo.toml | 2 +- crates/profile/Cargo.toml | 2 +- crates/project-model/Cargo.toml | 8 +- crates/rust-analyzer/Cargo.toml | 22 +- .../rust-analyzer/src/cli/analysis_stats.rs | 3 +- crates/rust-analyzer/src/config.rs | 13 +- .../src/integrated_benchmarks.rs | 2 + crates/rust-analyzer/src/reload.rs | 1 - crates/rustc-dependencies/Cargo.toml | 6 +- crates/sourcegen/Cargo.toml | 2 +- crates/stdx/Cargo.toml | 2 +- crates/syntax/Cargo.toml | 8 +- crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/edit_in_place.rs | 177 +++++++++++- crates/syntax/src/ast/generated/nodes.rs | 93 +++--- crates/syntax/src/ast/make.rs | 9 +- crates/syntax/src/ast/node_ext.rs | 9 + crates/test-utils/Cargo.toml | 2 +- crates/test-utils/src/minicore.rs | 11 +- crates/text-edit/Cargo.toml | 2 +- crates/vfs-notify/Cargo.toml | 3 +- crates/vfs/Cargo.toml | 2 +- docs/dev/lsp-extensions.md | 5 +- docs/user/generated_config.adoc | 7 +- docs/user/manual.adoc | 4 +- editors/code/package-lock.json | 10 +- editors/code/package.json | 18 +- editors/code/src/commands.ts | 22 -- editors/code/src/ctx.ts | 32 ++- editors/code/src/main.ts | 5 +- editors/code/tsconfig.json | 10 +- lib/line-index/Cargo.toml | 2 +- lib/lsp-server/Cargo.toml | 4 +- xtask/Cargo.toml | 4 +- 131 files changed, 2212 insertions(+), 970 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs create mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs create mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b5dbe30c3221c..6a3cdfe3a3c7f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -30,7 +30,6 @@ jobs: code-target: win32-x64 - os: windows-latest target: i686-pc-windows-msvc - code-target: win32-ia32 - os: windows-latest target: aarch64-pc-windows-msvc code-target: win32-arm64 @@ -102,12 +101,12 @@ jobs: working-directory: editors/code - name: Package Extension (release) - if: github.ref == 'refs/heads/release' + if: github.ref == 'refs/heads/release' && matrix.code-target run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} working-directory: editors/code - name: Package Extension (nightly) - if: github.ref != 'refs/heads/release' + if: github.ref != 'refs/heads/release' && matrix.code-target run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release working-directory: editors/code diff --git a/Cargo.lock b/Cargo.lock index fcb188c0dfab3..701e36d74a657 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,15 +28,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arbitrary" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "arrayvec" @@ -44,17 +44,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -101,9 +90,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "byteorder" @@ -131,9 +120,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", @@ -171,21 +160,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "264726159011fc7f22c23eb51f49021ece6e71bc358b96e7f2e842db0b14162b" +checksum = "c0322d5289ceba3217a03c9af72aa403d87542822b753daa1da32e4b992a4e80" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "synstructure", ] [[package]] name = "chalk-ir" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65c17407d4c756b8f7f84344acb0fb96364d0298822743219bb25769b6d00df" +checksum = "0946cbc6d9136980a24a2dddf1888b5f0aa978dda300a3aa470b55b777b6bf3c" dependencies = [ "bitflags 1.3.2", "chalk-derive", @@ -194,9 +183,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e2cf7b70bedaaf3a8cf3c93b6120c2bb65be89389124028e724d19e209686e" +checksum = "4cd93fedbeeadc0cd4d0eb73bd061b621af99f5324a6a518264c8ef5e526e0ec" dependencies = [ "chalk-derive", "chalk-ir", @@ -207,15 +196,15 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc67c548d3854f64e97e67dc5b7c88513425c5bfa347cff96b7992ae6379288" +checksum = "a254cff72303c58c82df421cfe9465606372b81588923fcf179922b7eaad9a53" dependencies = [ "chalk-derive", "chalk-ir", "ena", - "indexmap 1.9.3", - "itertools", + "indexmap 2.1.0", + "itertools 0.10.5", "petgraph", "rustc-hash", "tracing", @@ -315,20 +304,20 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] name = "dissimilar" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" +checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "dot" @@ -344,9 +333,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "ena" @@ -387,9 +376,9 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.2.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" @@ -455,9 +444,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -468,15 +457,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -497,7 +477,7 @@ dependencies = [ "hir-def", "hir-expand", "hir-ty", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "rustc-hash", @@ -514,7 +494,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.3.2", + "bitflags 2.4.1", "cfg", "cov-mark", "dashmap", @@ -524,9 +504,9 @@ dependencies = [ "fst", "hashbrown 0.12.3", "hir-expand", - "indexmap 2.0.0", + "indexmap 2.1.0", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", @@ -554,7 +534,7 @@ dependencies = [ "expect-test", "hashbrown 0.12.3", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", @@ -574,7 +554,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.3.2", + "bitflags 2.4.1", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -586,7 +566,7 @@ dependencies = [ "hir-def", "hir-expand", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "nohash-hasher", @@ -633,7 +613,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools", + "itertools 0.12.0", "nohash-hasher", "oorandom", "profile", @@ -659,7 +639,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "profile", "smallvec", "sourcegen", @@ -678,7 +658,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "smallvec", @@ -699,8 +679,8 @@ dependencies = [ "expect-test", "fst", "hir", - "indexmap 2.0.0", - "itertools", + "indexmap 2.1.0", + "itertools 0.12.0", "limit", "line-index 0.1.0-pre.1", "memchr", @@ -731,7 +711,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "serde_json", @@ -750,7 +730,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "nohash-hasher", "parser", "stdx", @@ -782,12 +762,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -838,6 +818,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.6" @@ -888,9 +877,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -942,7 +931,7 @@ dependencies = [ "crossbeam-channel", "ide", "ide-db", - "itertools", + "itertools 0.12.0", "proc-macro-api", "project-model", "tracing", @@ -1020,9 +1009,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -1123,7 +1112,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.3.2", + "bitflags 2.4.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1138,12 +1127,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" dependencies = [ - "overload", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1152,7 +1140,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1186,12 +1174,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" version = "0.11.2" @@ -1289,12 +1271,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.5.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.1.0", ] [[package]] @@ -1359,9 +1341,9 @@ version = "0.0.0" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1389,7 +1371,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "profile", @@ -1454,12 +1436,12 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7082716cb2bbcd8b5f062fe950cbbc87f3aba022d6da4168db35af6732a7f15d" +checksum = "9a2ea80a299f04a896000ce17b76f3aa1d2fe59f347fbc99c4b8970316ef5a0d" dependencies = [ "bitflags 1.3.2", - "ra-ap-rustc_index 0.18.0", + "ra-ap-rustc_index 0.19.0", "tracing", ] @@ -1475,9 +1457,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e14b1fc835d6992b128a03a3f3a8365ba9f03e1c656a1670305f63f30d786d" +checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" dependencies = [ "arrayvec", "smallvec", @@ -1495,9 +1477,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1762abb25eb1e37c1823f62b5da0821bbcd870812318db084c9516c2f78d2dcd" +checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" dependencies = [ "unicode-properties", "unicode-xid", @@ -1515,9 +1497,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -1525,14 +1507,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -1583,7 +1563,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", - "itertools", + "itertools 0.12.0", "load-cargo", "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", @@ -1634,8 +1614,8 @@ name = "rustc-dependencies" version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", - "ra-ap-rustc_index 0.18.0", - "ra-ap-rustc_lexer 0.18.0", + "ra-ap-rustc_index 0.19.0", + "ra-ap-rustc_lexer 0.19.0", "ra-ap-rustc_parse_format", ] @@ -1721,31 +1701,31 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.156" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.156" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 1.9.3", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -1759,7 +1739,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -1831,9 +1811,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -1848,7 +1828,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "unicode-xid", ] @@ -1859,8 +1839,8 @@ dependencies = [ "cov-mark", "either", "expect-test", - "indexmap 2.0.0", - "itertools", + "indexmap 2.1.0", + "itertools 0.12.0", "once_cell", "parser", "proc-macro2", @@ -1894,15 +1874,15 @@ dependencies = [ name = "text-edit" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.12.0", "text-size", ] [[package]] name = "text-size" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" @@ -1921,7 +1901,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -2005,11 +1985,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2017,20 +1996,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2038,20 +2017,20 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "sharded-slab", "thread_local", @@ -2061,11 +2040,10 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9742d8df709837409dbb22aa25dd7769c260406f20ff48a2320b80a4a6aed0" +checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" dependencies = [ - "atty", "nu-ansi-term", "tracing-core", "tracing-log", @@ -2175,7 +2153,7 @@ name = "vfs" version = "0.0.0" dependencies = [ "fst", - "indexmap 2.0.0", + "indexmap 2.1.0", "nohash-hasher", "paths", "rustc-hash", @@ -2388,18 +2366,18 @@ checksum = "f58e7b3ca8977093aae6b87b6a7730216fc4c53a6530bab5c43a783cd810c1a8" [[package]] name = "xshell" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90" +checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c" +checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" [[package]] name = "xtask" diff --git a/Cargo.toml b/Cargo.toml index c382a5a37d234..73bb9c84d2c95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,18 +90,35 @@ la-arena = { version = "0.3.1" } lsp-server = { version = "0.7.4" } # non-local crates +anyhow = "1.0.75" +bitflags = "2.4.1" +cargo_metadata = "0.18.1" +dissimilar = "1.0.7" +either = "1.9.0" +indexmap = "2.1.0" +itertools = "0.12.0" +libc = "0.2.150" smallvec = { version = "1.10.0", features = [ "const_new", "union", "const_generics", ] } +tracing = "0.1.40" +tracing-tree = "0.3.0" +tracing-subscriber = { version = "0.3.18", default-features = false, features = [ + "registry", + "fmt", + "tracing-log", +] } smol_str = "0.2.0" nohash-hasher = "0.2.0" -text-size = "1.1.0" -serde = { version = "1.0.156", features = ["derive"] } -serde_json = "1.0.96" +text-size = "1.1.1" +rayon = "1.8.0" +serde = { version = "1.0.192", features = ["derive"] } +serde_json = "1.0.108" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently hashbrown = { version = "0.12.3", features = [ "inline-more", ], default-features = false } +xshell = "0.2.5" diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index ed380897278b3..4324584df39d6 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -23,8 +23,8 @@ oorandom = "11.1.3" # We depend on both individually instead of using `features = ["derive"]` to microoptimize the # build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr` # supports `arbitrary`. This way, we avoid feature unification. -arbitrary = "1.3.0" -derive_arbitrary = "1.3.1" +arbitrary = "1.3.2" +derive_arbitrary = "1.3.2" # local deps mbe.workspace = true diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index e7f7adc784717..4322d2d966a11 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -12,9 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] +cargo_metadata.workspace = true crossbeam-channel = "0.5.8" -tracing = "0.1.37" -cargo_metadata = "0.15.4" +tracing.workspace = true rustc-hash = "1.1.0" serde_json.workspace = true serde.workspace = true diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 99b8e9bf0e14a..e4f2e14c51c72 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -13,19 +13,19 @@ doctest = false [dependencies] arrayvec = "0.7.2" -bitflags = "2.1.0" +bitflags.workspace = true cov-mark = "2.0.0-pre.1" # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } drop_bomb = "0.1.5" -either = "1.7.0" +either.workspace = true fst = { version = "0.4.7", default-features = false } -indexmap = "2.0.0" -itertools = "0.10.5" +indexmap.workspace = true +itertools.workspace = true la-arena.workspace = true once_cell = "1.17.0" rustc-hash = "1.1.0" -tracing = "0.1.35" +tracing.workspace = true smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index fad4d7a4da66e..6ecf1c20d6c1a 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -54,7 +54,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); - body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| { + body.params.iter().zip(db.function_data(it).params.iter()).for_each(|(¶m, ty)| { p.print_pat(param); p.buf.push(':'); p.print_type_ref(ty); diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 718f241cf780f..72ccc17486f0d 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -34,7 +34,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct FunctionData { pub name: Name, - pub params: Vec>, + pub params: Box<[Interned]>, pub ret_type: Interned, pub attrs: Attrs, pub visibility: RawVisibility, @@ -177,7 +177,7 @@ pub struct TypeAliasData { pub rustc_has_incoherent_inherent_impls: bool, pub rustc_allow_incoherent_impl: bool, /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). - pub bounds: Vec>, + pub bounds: Box<[Interned]>, } impl TypeAliasData { @@ -210,7 +210,7 @@ impl TypeAliasData { is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), rustc_has_incoherent_inherent_impls, rustc_allow_incoherent_impl, - bounds: typ.bounds.to_vec(), + bounds: typ.bounds.clone(), }) } } @@ -327,6 +327,7 @@ pub struct ImplData { pub self_ty: Interned, pub items: Vec, pub is_negative: bool, + pub is_unsafe: bool, // box it as the vec is usually empty anyways pub attribute_calls: Option, MacroCallId)>>>, } @@ -348,6 +349,7 @@ impl ImplData { let target_trait = impl_def.target_trait.clone(); let self_ty = impl_def.self_ty.clone(); let is_negative = impl_def.is_negative; + let is_unsafe = impl_def.is_unsafe; let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id)); @@ -357,7 +359,14 @@ impl ImplData { let items = items.into_iter().map(|(_, item)| item).collect(); ( - Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }), + Arc::new(ImplData { + target_trait, + self_ty, + items, + is_negative, + is_unsafe, + attribute_calls, + }), diagnostics.into(), ) } diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index b9c5ff72792af..1ebd1ba0e66db 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -21,9 +21,10 @@ pub fn find_path( item: ItemInNs, from: ModuleId, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { let _p = profile::span("find_path"); - find_path_inner(db, item, from, None, prefer_no_std) + find_path_inner(db, item, from, None, prefer_no_std, prefer_prelude) } pub fn find_path_prefixed( @@ -32,9 +33,10 @@ pub fn find_path_prefixed( from: ModuleId, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { let _p = profile::span("find_path_prefixed"); - find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std) + find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std, prefer_prelude) } #[derive(Copy, Clone, Debug)] @@ -88,6 +90,7 @@ fn find_path_inner( from: ModuleId, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { // - if the item is a builtin, it's in scope if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { @@ -109,6 +112,7 @@ fn find_path_inner( MAX_PATH_LEN, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, ) .map(|(item, _)| item); } @@ -134,6 +138,7 @@ fn find_path_inner( from, prefixed, prefer_no_std, + prefer_prelude, ) { let data = db.enum_data(variant.parent); path.push_segment(data.variants[variant.local_id].name.clone()); @@ -156,6 +161,7 @@ fn find_path_inner( from, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, scope_name, ) .map(|(item, _)| item) @@ -171,6 +177,7 @@ fn find_path_for_module( max_len: usize, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; @@ -236,6 +243,7 @@ fn find_path_for_module( from, prefixed, prefer_no_std, + prefer_prelude, scope_name, ) } @@ -316,6 +324,7 @@ fn calculate_best_path( from: ModuleId, mut prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, scope_name: Option, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { @@ -351,11 +360,14 @@ fn calculate_best_path( best_path_len - 1, prefixed, prefer_no_std, + prefer_prelude, ) { path.0.push_segment(name); let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), + Some(best_path) => { + select_best_path(best_path, path, prefer_no_std, prefer_prelude) + } None => path, }; best_path_len = new_path.0.len(); @@ -367,18 +379,18 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - let crate_graph = db.crate_graph(); - let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { + for dep in &db.crate_graph()[from.krate].dependencies { let import_map = db.import_map(dep.crate_id); - import_map.import_info_for(item).and_then(|info| { + let Some(import_info_for) = import_map.import_info_for(item) else { continue }; + for info in import_info_for { if info.is_doc_hidden { // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate - return None; + continue; } // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let (mut path, path_stability) = find_path_for_module( + let Some((mut path, path_stability)) = find_path_for_module( db, def_map, visited_modules, @@ -388,22 +400,26 @@ fn calculate_best_path( max_len - 1, prefixed, prefer_no_std, - )?; + prefer_prelude, + ) else { + continue; + }; cov_mark::hit!(partially_imported); path.push_segment(info.name.clone()); - Some(( + + let path_with_stab = ( path, zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }), - )) - }) - }); + ); - for path in extern_paths { - let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), - None => path, - }; - update_best_path(&mut best_path, new_path); + let new_path_with_stab = match best_path.take() { + Some(best_path) => { + select_best_path(best_path, path_with_stab, prefer_no_std, prefer_prelude) + } + None => path_with_stab, + }; + update_best_path(&mut best_path, new_path_with_stab); + } } } if let Some(module) = item.module(db) { @@ -420,17 +436,39 @@ fn calculate_best_path( } } +/// Select the best (most relevant) path between two paths. +/// This accounts for stability, path length whether std should be chosen over alloc/core paths as +/// well as ignoring prelude like paths or not. fn select_best_path( - old_path: (ModPath, Stability), - new_path: (ModPath, Stability), + old_path @ (_, old_stability): (ModPath, Stability), + new_path @ (_, new_stability): (ModPath, Stability), prefer_no_std: bool, + prefer_prelude: bool, ) -> (ModPath, Stability) { - match (old_path.1, new_path.1) { + match (old_stability, new_stability) { (Stable, Unstable) => return old_path, (Unstable, Stable) => return new_path, _ => {} } const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; + + let choose = |new_path: (ModPath, _), old_path: (ModPath, _)| { + let new_has_prelude = new_path.0.segments().iter().any(|seg| seg == &known::prelude); + let old_has_prelude = old_path.0.segments().iter().any(|seg| seg == &known::prelude); + match (new_has_prelude, old_has_prelude, prefer_prelude) { + (true, false, true) | (false, true, false) => new_path, + (true, false, false) | (false, true, true) => old_path, + // no prelude difference in the paths, so pick the smaller one + (true, true, _) | (false, false, _) => { + if new_path.0.len() < old_path.0.len() { + new_path + } else { + old_path + } + } + } + }; + match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { let rank = match prefer_no_std { @@ -451,23 +489,11 @@ fn select_best_path( let orank = rank(old); match nrank.cmp(&orank) { Ordering::Less => old_path, - Ordering::Equal => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + Ordering::Equal => choose(new_path, old_path), Ordering::Greater => new_path, } } - _ => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + _ => choose(new_path, old_path), } } @@ -570,7 +596,13 @@ mod tests { /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the /// module the cursor is in. - fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option) { + #[track_caller] + fn check_found_path_( + ra_fixture: &str, + path: &str, + prefix_kind: Option, + prefer_prelude: bool, + ) { let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_at_position(pos); let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};")); @@ -589,11 +621,17 @@ mod tests { ) .0 .take_types() - .unwrap(); - - let found_path = - find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false); - assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}"); + .expect("path does not resolve to a type"); + + let found_path = find_path_inner( + &db, + ItemInNs::Types(resolved), + module, + prefix_kind, + false, + prefer_prelude, + ); + assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); } fn check_found_path( @@ -603,10 +641,23 @@ mod tests { absolute: &str, self_prefixed: &str, ) { - check_found_path_(ra_fixture, unprefixed, None); - check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain)); - check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate)); - check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf)); + check_found_path_(ra_fixture, unprefixed, None, false); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), false); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), false); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), false); + } + + fn check_found_path_prelude( + ra_fixture: &str, + unprefixed: &str, + prefixed: &str, + absolute: &str, + self_prefixed: &str, + ) { + check_found_path_(ra_fixture, unprefixed, None, true); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), true); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), true); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), true); } #[test] @@ -1421,4 +1472,34 @@ pub mod error { "std::error::Error", ); } + + #[test] + fn respects_prelude_setting() { + let ra_fixture = r#" +//- /main.rs crate:main deps:krate +$0 +//- /krate.rs crate:krate +pub mod prelude { + pub use crate::foo::*; +} + +pub mod foo { + pub struct Foo; +} +"#; + check_found_path( + ra_fixture, + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + ); + check_found_path_prelude( + ra_fixture, + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + ); + } } diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index 1e2535a8a9993..fac90e6630452 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -227,7 +227,7 @@ impl GenericParams { let mut expander = Lazy::new(|| { (module.def_map(db), Expander::new(db, loc.source(db).file_id, module)) }); - for param in &func_data.params { + for param in func_data.params.iter() { generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); } diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index c0b507d3911ac..d589fbe347a9f 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -1,13 +1,14 @@ //! A map of all publicly exported items in a crate. -use std::{collections::hash_map::Entry, fmt, hash::BuildHasherDefault}; +use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; -use fst::{self, Streamer}; +use fst::{self, raw::IndexedValue, Streamer}; use hir_expand::name::Name; use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use rustc_hash::{FxHashSet, FxHasher}; +use smallvec::SmallVec; use triomphe::Arc; use crate::{ @@ -20,31 +21,28 @@ use crate::{ type FxIndexMap = IndexMap>; -// FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should -// have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`. /// Item import details stored in the `ImportMap`. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct ImportInfo { /// A name that can be used to import the item, relative to the crate's root. pub name: Name, /// The module containing this item. pub container: ModuleId, - /// Whether the import is a trait associated item or not. - pub is_trait_assoc_item: bool, /// Whether this item is annotated with `#[doc(hidden)]`. pub is_doc_hidden: bool, /// Whether this item is annotated with `#[unstable(..)]`. pub is_unstable: bool, } +type ImportMapIndex = FxIndexMap, IsTraitAssocItem)>; + /// A map from publicly exported items to its name. /// /// Reexports of items are taken into account, ie. if something is exported under multiple /// names, the one with the shortest import path will be used. #[derive(Default)] pub struct ImportMap { - map: FxIndexMap, - + map: ImportMapIndex, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// @@ -55,6 +53,12 @@ pub struct ImportMap { fst: fst::Map>, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum IsTraitAssocItem { + Yes, + No, +} + impl ImportMap { pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("import_map_query"); @@ -64,13 +68,18 @@ impl ImportMap { let mut importables: Vec<_> = map .iter() // We've only collected items, whose name cannot be tuple field. - .map(|(&item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + .flat_map(|(&item, (info, _))| { + info.iter() + .map(move |info| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + }) .collect(); importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); + importables.dedup(); // Build the FST, taking care not to insert duplicate values. let mut builder = fst::MapBuilder::memory(); - let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1); + let iter = + importables.iter().enumerate().dedup_by(|(_, (_, lhs)), (_, (_, rhs))| lhs == rhs); for (start_idx, (_, name)) in iter { let _ = builder.insert(name, start_idx as u64); } @@ -82,12 +91,12 @@ impl ImportMap { }) } - pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { - self.map.get(&item) + pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { + self.map.get(&item).map(|(info, _)| &**info) } } -fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap { +fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex { let _p = profile::span("collect_import_map"); let def_map = db.crate_def_map(krate); @@ -95,11 +104,13 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap FxIndexMap Some(id.into()), } }; - let status @ (is_doc_hidden, is_unstable) = - attr_id.map_or((false, false), |attr_id| { - let attrs = db.attrs(attr_id); - (attrs.has_doc_hidden(), attrs.is_unstable()) - }); + let (is_doc_hidden, is_unstable) = attr_id.map_or((false, false), |attr_id| { + let attrs = db.attrs(attr_id); + (attrs.has_doc_hidden(), attrs.is_unstable()) + }); let import_info = ImportInfo { name: name.clone(), container: module, - is_trait_assoc_item: false, is_doc_hidden, is_unstable, }; - match depth_map.entry(item) { - Entry::Vacant(entry) => _ = entry.insert((depth, status)), - Entry::Occupied(mut entry) => { - let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get(); - (depth, occ_depth); - let overwrite = match ( - is_doc_hidden, - occ_is_doc_hidden, - is_unstable, - occ_is_unstable, - ) { - // no change of hiddeness or unstableness - (true, true, true, true) - | (true, true, false, false) - | (false, false, true, true) - | (false, false, false, false) => depth < occ_depth, - - // either less hidden or less unstable, accept - (true, true, false, true) - | (false, true, true, true) - | (false, true, false, true) - | (false, true, false, false) - | (false, false, false, true) => true, - // more hidden or unstable, discard - (true, true, true, false) - | (true, false, true, true) - | (true, false, true, false) - | (true, false, false, false) - | (false, false, true, false) => false, - - // exchanges doc(hidden) for unstable (and vice-versa), - (true, false, false, true) | (false, true, true, false) => { - depth < occ_depth - } - }; - if !overwrite { - continue; - } - entry.insert((depth, status)); - } - } - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { collect_trait_assoc_items( db, @@ -193,13 +160,14 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap FxIndexMap, + map: &mut ImportMapIndex, tr: TraitId, is_type_in_ns: bool, trait_import_info: &ImportInfo, @@ -237,11 +205,14 @@ fn collect_trait_assoc_items( let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), - is_trait_assoc_item: true, is_doc_hidden: attrs.has_doc_hidden(), is_unstable: attrs.is_unstable(), }; - map.insert(assoc_item, assoc_item_info); + + let (infos, _) = + map.entry(assoc_item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::Yes)); + infos.reserve_exact(1); + infos.push(assoc_item_info); } } @@ -259,10 +230,13 @@ impl fmt::Debug for ImportMap { let mut importable_names: Vec<_> = self .map .iter() - .map(|(item, _)| match item { - ItemInNs::Types(it) => format!("- {it:?} (t)",), - ItemInNs::Values(it) => format!("- {it:?} (v)",), - ItemInNs::Macros(it) => format!("- {it:?} (m)",), + .map(|(item, (infos, _))| { + let l = infos.len(); + match item { + ItemInNs::Types(it) => format!("- {it:?} (t) [{l}]",), + ItemInNs::Values(it) => format!("- {it:?} (v) [{l}]",), + ItemInNs::Macros(it) => format!("- {it:?} (m) [{l}]",), + } }) .collect(); @@ -272,7 +246,7 @@ impl fmt::Debug for ImportMap { } /// A way to match import map contents against the search query. -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] enum SearchMode { /// Import map entry should strictly match the query string. Exact, @@ -345,6 +319,15 @@ impl Query { Self { case_sensitive: true, ..self } } + fn matches_assoc_mode(&self, is_trait_assoc_item: IsTraitAssocItem) -> bool { + match (is_trait_assoc_item, self.assoc_mode) { + (IsTraitAssocItem::Yes, AssocSearchMode::Exclude) + | (IsTraitAssocItem::No, AssocSearchMode::AssocItemsOnly) => false, + _ => true, + } + } + + /// Checks whether the import map entry matches the query. fn import_matches( &self, db: &dyn DefDatabase, @@ -352,12 +335,8 @@ impl Query { enforce_lowercase: bool, ) -> bool { let _p = profile::span("import_map::Query::import_matches"); - match (import.is_trait_assoc_item, self.assoc_mode) { - (true, AssocSearchMode::Exclude) => return false, - (false, AssocSearchMode::AssocItemsOnly) => return false, - _ => {} - } + // FIXME: Can we get rid of the alloc here? let mut input = import.name.display(db.upcast()).to_string(); let case_insensitive = enforce_lowercase || !self.case_sensitive; if case_insensitive { @@ -388,7 +367,7 @@ impl Query { pub fn search_dependencies( db: &dyn DefDatabase, krate: CrateId, - query: Query, + ref query: Query, ) -> FxHashSet { let _p = profile::span("search_dependencies").detail(|| format!("{query:?}")); @@ -406,31 +385,58 @@ pub fn search_dependencies( let mut stream = op.union(); let mut res = FxHashSet::default(); + let mut common_importable_data_scratch = vec![]; while let Some((_, indexed_values)) = stream.next() { - for indexed_value in indexed_values { - let import_map = &import_maps[indexed_value.index]; - let importables = &import_map.importables[indexed_value.value as usize..]; + for &IndexedValue { index, value } in indexed_values { + let import_map = &import_maps[index]; + let importables @ [importable, ..] = &import_map.importables[value as usize..] else { + continue; + }; - let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(db, common_importable_data, true) { + let &(ref importable_data, is_trait_assoc_item) = &import_map.map[importable]; + if !query.matches_assoc_mode(is_trait_assoc_item) { continue; } - // Name shared by the importable items in this group. - let common_importable_name = - common_importable_data.name.to_smol_str().to_ascii_lowercase(); - // Add the items from this name group. Those are all subsequent items in - // `importables` whose name match `common_importable_name`. - let iter = importables - .iter() - .copied() - .take_while(|item| { - common_importable_name - == import_map.map[item].name.to_smol_str().to_ascii_lowercase() - }) - .filter(|item| { - !query.case_sensitive // we've already checked the common importables name case-insensitively - || query.import_matches(db, &import_map.map[item], false) + common_importable_data_scratch.extend( + importable_data + .iter() + .filter(|&info| query.import_matches(db, info, true)) + // Name shared by the importable items in this group. + .map(|info| info.name.to_smol_str()), + ); + if common_importable_data_scratch.is_empty() { + continue; + } + common_importable_data_scratch.sort(); + common_importable_data_scratch.dedup(); + + let iter = + common_importable_data_scratch.drain(..).flat_map(|common_importable_name| { + // Add the items from this name group. Those are all subsequent items in + // `importables` whose name match `common_importable_name`. + importables + .iter() + .copied() + .take_while(move |item| { + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos.iter().any(|info| { + info.name + .to_smol_str() + .eq_ignore_ascii_case(&common_importable_name) + }) + }) + .filter(move |item| { + !query.case_sensitive || { + // we've already checked the common importables name case-insensitively + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos + .iter() + .any(|info| query.import_matches(db, info, false)) + } + }) }); res.extend(iter); @@ -457,6 +463,7 @@ mod tests { let mut importable_paths: Vec<_> = self .map .iter() + .flat_map(|(item, (info, _))| info.iter().map(move |info| (item, info))) .map(|(item, info)| { let path = render_path(db, info); let ns = match item { @@ -495,7 +502,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - render_path(&db, dependency_imports.import_info_for(dependency)?), + render_path(&db, &dependency_imports.import_info_for(dependency)?[0]), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -543,7 +550,12 @@ mod tests { .items .iter() .find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?; - Some(format!("{}::{}", render_path(db, trait_info), assoc_item_name.display(db.upcast()))) + // FIXME: This should check all import infos, not just the first + Some(format!( + "{}::{}", + render_path(db, &trait_info[0]), + assoc_item_name.display(db.upcast()) + )) } fn check(ra_fixture: &str, expect: Expect) { @@ -619,6 +631,7 @@ mod tests { main: - publ1 (t) - real_pu2 (t) + - real_pu2::Pub (t) - real_pub (t) - real_pub::Pub (t) "#]], @@ -644,6 +657,7 @@ mod tests { - sub (t) - sub::Def (t) - sub::subsub (t) + - sub::subsub::Def (t) "#]], ); } @@ -743,7 +757,9 @@ mod tests { - module (t) - module::S (t) - module::S (v) + - module::module (t) - sub (t) + - sub::module (t) "#]], ); } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 3c4f21d5c69b6..70b96b25739cb 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -709,6 +709,7 @@ pub struct Impl { pub target_trait: Option>, pub self_ty: Interned, pub is_negative: bool, + pub is_unsafe: bool, pub items: Box<[AssocItem]>, pub ast_id: FileAstId, } diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index c0a880a64bb63..c898eb5f92112 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -396,14 +396,7 @@ impl<'a> Ctx<'a> { let bounds = self.lower_type_bounds(type_alias); let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias); let ast_id = self.source_ast_id_map.ast_id(type_alias); - let res = TypeAlias { - name, - visibility, - bounds: bounds.into_boxed_slice(), - generic_params, - type_ref, - ast_id, - }; + let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; Some(id(self.data().type_aliases.alloc(res))) } @@ -499,6 +492,7 @@ impl<'a> Ctx<'a> { let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let self_ty = self.lower_type_ref(&impl_def.self_ty()?); let is_negative = impl_def.excl_token().is_some(); + let is_unsafe = impl_def.unsafe_token().is_some(); // We cannot use `assoc_items()` here as that does not include macro calls. let items = impl_def @@ -513,7 +507,8 @@ impl<'a> Ctx<'a> { }) .collect(); let ast_id = self.source_ast_id_map.ast_id(impl_def); - let res = Impl { generic_params, target_trait, self_ty, is_negative, items, ast_id }; + let res = + Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id }; Some(id(self.data().impls.alloc(res))) } @@ -637,13 +632,13 @@ impl<'a> Ctx<'a> { Interned::new(generics) } - fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Vec> { + fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned]> { match node.type_bound_list() { Some(bound_list) => bound_list .bounds() .map(|it| Interned::new(TypeBound::from_ast(&self.body_ctx, it))) .collect(), - None => Vec::new(), + None => Box::default(), } } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 5036c2b88297f..ca3785bf28dfa 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -388,8 +388,18 @@ impl Printer<'_> { wln!(self); } ModItem::Impl(it) => { - let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } = - &self.tree[it]; + let Impl { + target_trait, + self_ty, + is_negative, + is_unsafe, + items, + generic_params, + ast_id: _, + } = &self.tree[it]; + if *is_unsafe { + w!(self, "unsafe"); + } w!(self, "impl"); self.print_generic_params(generic_params); w!(self, " "); diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 495e2d476970c..fd8f64d6705b0 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -152,7 +152,7 @@ impl TryFrom for CrateRootModuleId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ModuleId { krate: CrateId, /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 4aedb22c6bc9a..106ead83fad76 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -17,7 +17,7 @@ fn main() { column!(); } #[rustc_builtin_macro] macro_rules! column {() => {}} -fn main() { 0 as u32; } +fn main() { 0u32; } "#]], ); } @@ -74,7 +74,7 @@ fn main() { line!() } #[rustc_builtin_macro] macro_rules! line {() => {}} -fn main() { 0 as u32 } +fn main() { 0u32 } "#]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index b416f45ff208e..2886b2a366c04 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -970,3 +970,37 @@ builtin #format_args ("{}", &[0 2]); "##]], ); } + +#[test] +fn eager_concat_line() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat {} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! line {} + +fn main() { + concat!("event ", line!()); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat {} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! line {} + +fn main() { + "event 0u32"; +} + +"##]], + ); +} diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml index 1f27204c1917b..361bbec4318f3 100644 --- a/crates/hir-expand/Cargo.toml +++ b/crates/hir-expand/Cargo.toml @@ -13,11 +13,11 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -tracing = "0.1.35" -either = "1.7.0" +tracing.workspace = true +either.workspace = true rustc-hash = "1.1.0" la-arena.workspace = true -itertools = "0.10.5" +itertools.workspace = true hashbrown.workspace = true smallvec.workspace = true triomphe.workspace = true diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 30b19b6e51b8b..a04de10b899cf 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -78,7 +78,7 @@ pub fn find_builtin_macro( register_builtin! { LAZY: - (column, Column) => column_expand, + (column, Column) => line_expand, (file, File) => file_expand, (line, Line) => line_expand, (module_path, ModulePath) => module_path_expand, @@ -127,11 +127,13 @@ fn line_expand( _tt: &tt::Subtree, ) -> ExpandResult { // dummy implementation for type-checking purposes - let expanded = quote! { - 0 as u32 - }; - - ExpandResult::ok(expanded) + ExpandResult::ok(tt::Subtree { + delimiter: tt::Delimiter::unspecified(), + token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text: "0u32".into(), + span: tt::Span::UNSPECIFIED, + }))], + }) } fn log_syntax_expand( @@ -164,19 +166,6 @@ fn stringify_expand( ExpandResult::ok(expanded) } -fn column_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, - _tt: &tt::Subtree, -) -> ExpandResult { - // dummy implementation for type-checking purposes - let expanded = quote! { - 0 as u32 - }; - - ExpandResult::ok(expanded) -} - fn assert_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 204227e33873e..ff0d279d8cce1 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -12,15 +12,11 @@ use syntax::{ use triomphe::Arc; use crate::{ - ast_id_map::AstIdMap, - builtin_attr_macro::pseudo_derive_attr_expansion, - builtin_fn_macro::EagerExpander, - fixup, - hygiene::HygieneFrame, - name::{name, AsName}, - tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, - ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, - MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, + ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, + builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, AstId, BuiltinAttrExpander, + BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult, + ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, + MacroDefKind, MacroFile, ProcMacroExpander, }; /// Total limit on the number of tokens produced by any macro invocation. @@ -619,20 +615,7 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { - if let Some(name_ref) = - ast_id.to_node(db).path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) - { - name_ref.as_name() == name!(include) - } else { - false - } - } - _ => false, - }; - - if !skip_check_tt_count { + if !loc.def.is_include() { // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value; diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index c30807ad88490..f2d2451511f7c 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -13,20 +13,20 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true arrayvec = "0.7.2" -bitflags = "2.1.0" +bitflags.workspace = true smallvec.workspace = true ena = "0.14.0" -either = "1.7.0" +either.workspace = true oorandom = "11.1.3" -tracing = "0.1.35" +tracing.workspace = true rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.93.0", default-features = false } -chalk-ir = "0.93.0" -chalk-recursive = { version = "0.93.0", default-features = false } -chalk-derive = "0.93.0" +chalk-solve = { version = "0.94.0", default-features = false } +chalk-ir = "0.94.0" +chalk-recursive = { version = "0.94.0", default-features = false } +chalk-derive = "0.94.0" la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true @@ -47,11 +47,9 @@ limit.workspace = true [dev-dependencies] expect-test = "1.4.0" -tracing = "0.1.35" -tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "registry", -] } -tracing-tree = "0.2.1" +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true project-model = { path = "../project-model" } # local deps diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index c0b243ea24856..c9ab356854b82 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -28,6 +28,7 @@ pub trait TyExt { fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; + fn is_union(&self) -> bool; fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_builtin(&self) -> Option; @@ -96,6 +97,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::InferenceVar(_, _)) } + fn is_union(&self) -> bool { + matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_)))) + } + fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { match self.kind(Interner) { TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 9c96b5ab8dbb6..410bcbf0356f0 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -20,8 +20,8 @@ use crate::{ method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, - TyDefId, ValueTyDefId, + Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, + TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -47,7 +47,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: DefWithBodyId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] @@ -55,7 +55,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: ClosureId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::borrowck_query)] @@ -81,7 +81,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: GeneralConstId, subst: Substitution, - trait_env: Option>, + trait_env: Option>, ) -> Result; #[salsa::invoke(crate::consteval::const_eval_static_query)] @@ -104,16 +104,12 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: AdtId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(crate::layout::layout_of_ty_recover)] - fn layout_of_ty( - &self, - ty: Ty, - env: Arc, - ) -> Result, LayoutError>; + fn layout_of_ty(&self, ty: Ty, env: Arc) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; @@ -121,7 +117,7 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, - env: Arc, + env: Arc, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution); @@ -149,10 +145,10 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] - fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; #[salsa::invoke(crate::lower::trait_environment_query)] - fn trait_environment(&self, def: GenericDefId) -> Arc; + fn trait_environment(&self, def: GenericDefId) -> Arc; #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::cycle(crate::lower::generic_defaults_recover)] @@ -249,7 +245,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn normalize_projection( &self, projection: crate::ProjectionTy, - env: Arc, + env: Arc, ) -> Ty; #[salsa::invoke(trait_solve_wait)] diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs index ef43ed5c463fc..c1b3619009bd4 100644 --- a/crates/hir-ty/src/diagnostics.rs +++ b/crates/hir-ty/src/diagnostics.rs @@ -11,9 +11,3 @@ pub use crate::diagnostics::{ }, unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr}, }; - -#[derive(Debug, PartialEq, Eq)] -pub struct IncoherentImpl { - pub file_id: hir_expand::HirFileId, - pub impl_: syntax::AstPtr, -} diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index b432588b23089..f4c079b48c58c 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -19,8 +19,8 @@ use hir_def::{ data::adt::VariantData, hir::{Pat, PatId}, src::HasSource, - AdtId, AttrDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, ItemContainerId, - Lookup, ModuleDefId, ModuleId, StaticId, StructId, + AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + StaticId, StructId, }; use hir_expand::{ name::{AsName, Name}, @@ -290,8 +290,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(func.into()); - // Check whether non-snake case identifiers are allowed for this function. if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { return; @@ -568,11 +566,6 @@ impl<'a> DeclValidator<'a> { fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); - for (local_id, _) in data.variants.iter() { - let variant_id = EnumVariantId { parent: enum_id, local_id }; - self.validate_body_inner_items(variant_id.into()); - } - // Check whether non-camel case names are allowed for this enum. if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { return; @@ -697,8 +690,6 @@ impl<'a> DeclValidator<'a> { fn validate_const(&mut self, const_id: ConstId) { let data = self.db.const_data(const_id); - self.validate_body_inner_items(const_id.into()); - if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -747,8 +738,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(static_id.into()); - if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -786,17 +775,4 @@ impl<'a> DeclValidator<'a> { self.sink.push(diagnostic); } - - // FIXME: We don't currently validate names within `DefWithBodyId::InTypeConstId`. - /// Recursively validates inner scope items, such as static variables and constants. - fn validate_body_inner_items(&mut self, body_id: DefWithBodyId) { - let body = self.db.body(body_id); - for (_, block_def_map) in body.blocks(self.db.upcast()) { - for (_, module) in block_def_map.modules() { - for def_id in module.scope.declarations() { - self.validate_item(def_id); - } - } - } - } } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index f6d6b00d740a7..9ccf467358ece 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -945,6 +945,7 @@ impl HirDisplay for Ty { ItemInNs::Types((*def_id).into()), module_id, false, + true, ) { write!(f, "{}", path.display(f.db.upcast()))?; } else { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 0805e20447a73..af74df1032cb8 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -735,6 +735,32 @@ impl InferenceContext<'_> { self.walk_expr(expr); } + fn restrict_precision_for_unsafe(&mut self) { + for capture in &mut self.current_captures { + let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(0); + continue; + } + for (i, p) in capture.place.projections.iter().enumerate() { + ty = p.projected_ty( + ty, + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db.upcast()).krate(), + ); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(i + 1); + break; + } + } + } + } + fn adjust_for_move_closure(&mut self) { for capture in &mut self.current_captures { if let Some(first_deref) = @@ -924,6 +950,7 @@ impl InferenceContext<'_> { self.result.mutated_bindings_in_closure.insert(item.place.local); } } + self.restrict_precision_for_unsafe(); // closure_kind should be done before adjust_for_move_closure let closure_kind = self.closure_kind(); match capture_by { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 603e58f9d4620..b2591f016d429 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -1,5 +1,7 @@ //! Compute the binary representation of a type +use std::fmt; + use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ @@ -26,12 +28,6 @@ pub use self::{ target::target_data_layout_query, }; -macro_rules! user_error { - ($it: expr) => { - return Err(LayoutError::UserError(format!($it).into())) - }; -} - mod adt; mod target; @@ -73,13 +69,38 @@ pub type Variants = hir_def::layout::Variants), - SizeOverflow, - TargetLayoutNotAvailable, - HasPlaceholder, + HasErrorConst, HasErrorType, + HasPlaceholder, + InvalidSimdType, NotImplemented, + RecursiveTypeWithoutIndirection, + SizeOverflow, + TargetLayoutNotAvailable, Unknown, + UserReprTooSmall, +} + +impl std::error::Error for LayoutError {} +impl fmt::Display for LayoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), + LayoutError::HasErrorType => write!(f, "type contains an error"), + LayoutError::HasPlaceholder => write!(f, "type contains placeholders"), + LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"), + LayoutError::NotImplemented => write!(f, "not implemented"), + LayoutError::RecursiveTypeWithoutIndirection => { + write!(f, "recursive type without indirection") + } + LayoutError::SizeOverflow => write!(f, "size overflow"), + LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"), + LayoutError::Unknown => write!(f, "unknown"), + LayoutError::UserReprTooSmall => { + write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum") + } + } + } } struct LayoutCx<'a> { @@ -118,9 +139,7 @@ fn layout_of_simd_ty( let f0_ty = match fields.iter().next() { Some(it) => it.1.clone().substitute(Interner, subst), - None => { - user_error!("simd type with zero fields"); - } + None => return Err(LayoutError::InvalidSimdType), }; // The element type and number of elements of the SIMD vector @@ -134,7 +153,7 @@ fn layout_of_simd_ty( // Extract the number of elements from the layout of the array field: let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { - user_error!("Array with non array layout"); + return Err(LayoutError::Unknown); }; (e_ty.clone(), count, true) @@ -146,7 +165,7 @@ fn layout_of_simd_ty( // Compute the ABI of the element type: let e_ly = db.layout_of_ty(e_ty, env.clone())?; let Abi::Scalar(e_abi) = e_ly.abi else { - user_error!("simd type with inner non scalar type"); + return Err(LayoutError::Unknown); }; // Compute the size and alignment of the vector: @@ -259,9 +278,7 @@ pub fn layout_of_ty_query( cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from( - "unevaluated or mistyped const generic parameter", - )))? as u64; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; @@ -352,7 +369,7 @@ pub fn layout_of_ty_query( let mut unit = layout_of_unit(&cx, dl)?; match unit.abi { Abi::Aggregate { ref mut sized } => *sized = false, - _ => user_error!("bug"), + _ => return Err(LayoutError::Unknown), } unit } @@ -418,7 +435,7 @@ pub fn layout_of_ty_recover( _: &Ty, _: &Arc, ) -> Result, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 5e713c17cf81f..58a06dc643542 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -145,7 +145,7 @@ pub fn layout_of_adt_recover( _: &Substitution, _: &Arc, ) -> Result, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } /// Finds the appropriate Integer type and signedness for the given @@ -169,11 +169,7 @@ fn repr_discr( let discr = Integer::from_attr(dl, ity); let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; if discr < fit { - return Err(LayoutError::UserError( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum " - .into(), - )); + return Err(LayoutError::UserReprTooSmall); } return Ok((discr, ity.is_signed())); } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index ffdbb9de934dc..5e3a86c80e3fc 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -210,16 +210,13 @@ fn recursive() { struct BoxLike(*mut T); struct Goal(BoxLike); } - check_fail( - r#"struct Goal(Goal);"#, - LayoutError::UserError("infinite sized recursive type".into()), - ); + check_fail(r#"struct Goal(Goal);"#, LayoutError::RecursiveTypeWithoutIndirection); check_fail( r#" struct Foo(Foo); struct Goal(Foo); "#, - LayoutError::UserError("infinite sized recursive type".into()), + LayoutError::RecursiveTypeWithoutIndirection, ); } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index c14339f6afe03..bcf7bfa0d2730 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -81,6 +81,7 @@ pub use mapping::{ lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, to_placeholder_idx, }; +pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; pub use utils::{all_super_traits, is_fn_unsafe_to_call}; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 9a61f15359932..c8a85b4a9ff09 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1383,51 +1383,50 @@ pub(crate) fn generic_predicates_for_param_query( let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); let generics = generics(db.upcast(), def); - let mut predicates: Vec<_> = resolver - .where_predicates_in_scope() - // we have to filter out all other predicates *first*, before attempting to lower them - .filter(|pred| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - match target { - WherePredicateTypeTarget::TypeRef(type_ref) => { - if ctx.lower_ty_only_param(type_ref) != Some(param_id) { - return false; - } - } - &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { - let target_id = TypeOrConstParamId { parent: def, local_id }; - if target_id != param_id { - return false; - } - } - }; - match &**bound { - TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &&_| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = match target { + WherePredicateTypeTarget::TypeRef(type_ref) => { + ctx.lower_ty_only_param(type_ref) != Some(param_id) + } + &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { + let target_id = TypeOrConstParamId { parent: def, local_id }; + target_id != param_id + } + }; + if invalid_target { + return false; + } + + match &**bound { + TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. - let assoc_name = match &assoc_name { - Some(it) => it, - None => return true, - }; - let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) { - Some(TypeNs::TraitId(tr)) => tr, - _ => return false, - }; + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db.upcast(), path) + else { + return false; + }; - all_super_traits(db.upcast(), tr).iter().any(|tr| { - db.trait_data(*tr).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) + all_super_traits(db.upcast(), tr).iter().any(|tr| { + db.trait_data(*tr).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) - } - TypeBound::Lifetime(_) | TypeBound::Error => false, + }) } + TypeBound::Lifetime(_) | TypeBound::Error => false, } - WherePredicate::Lifetime { .. } => false, - }) + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates: Vec<_> = resolver + .where_predicates_in_scope() + .filter(predicate) .flat_map(|pred| { ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)) }) @@ -1519,7 +1518,12 @@ pub(crate) fn trait_environment_query( let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env }) + Arc::new(TraitEnvironment { + krate, + block: None, + traits_from_clauses: traits_in_scope.into_boxed_slice(), + env, + }) } /// Resolve the where clause(s) of an item with generics. diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index f3a5f69b2a63c..87c93283361fc 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -862,6 +862,62 @@ fn is_inherent_impl_coherent( } } +/// Checks whether the impl satisfies the orphan rules. +/// +/// Given `impl Trait for T0`, an `impl`` is valid only if at least one of the following is true: +/// - Trait is a local trait +/// - All of +/// - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. +/// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) +pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { + let substs = TyBuilder::placeholder_subst(db, impl_); + let Some(impl_trait) = db.impl_trait(impl_) else { + // not a trait impl + return true; + }; + + let local_crate = impl_.lookup(db.upcast()).container.krate(); + let is_local = |tgt_crate| tgt_crate == local_crate; + + let trait_ref = impl_trait.substitute(Interner, &substs); + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if is_local(trait_id.module(db.upcast()).krate()) { + // trait to be implemented is local + return true; + } + + let unwrap_fundamental = |ty: Ty| match ty.kind(Interner) { + TyKind::Ref(_, _, referenced) => referenced.clone(), + &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => { + let struct_data = db.struct_data(s); + if struct_data.flags.contains(StructFlags::IS_FUNDAMENTAL) { + let next = subs.type_parameters(Interner).next(); + match next { + Some(ty) => ty, + None => ty, + } + } else { + ty + } + } + _ => ty, + }; + // - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. + let is_not_orphan = trait_ref.substitution.type_parameters(Interner).any(|ty| { + match unwrap_fundamental(ty).kind(Interner) { + &TyKind::Adt(AdtId(id), _) => is_local(id.module(db.upcast()).krate()), + TyKind::Error => true, + TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { + is_local(from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()) + }), + _ => false, + } + }); + // FIXME: param coverage + // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) + is_not_orphan +} + pub fn iterate_path_candidates( ty: &Canonical, db: &dyn HirDatabase, diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 1e6e946a13f8b..d16e0eb0137bf 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -684,8 +684,7 @@ fn infer_builtin_macros_line() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 63..87 '{ ...!(); }': () 73..74 'x': u32 "#]], @@ -723,8 +722,7 @@ fn infer_builtin_macros_column() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 65..91 '{ ...!(); }': () 75..76 'x': u32 "#]], diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index d36b885ec14a4..48dd9540329d6 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4439,42 +4439,42 @@ fn test(v: S) { fn associated_type_in_argument() { check( r#" - trait A { - fn m(&self) -> i32; - } +trait A { + fn m(&self) -> i32; +} - fn x(k: &::Ty) { - k.m(); - } +fn x(k: &::Ty) { + k.m(); +} - struct X; - struct Y; +struct X; +struct Y; - impl A for X { - fn m(&self) -> i32 { - 8 - } +impl A for X { + fn m(&self) -> i32 { + 8 } +} - impl A for Y { - fn m(&self) -> i32 { - 32 - } +impl A for Y { + fn m(&self) -> i32 { + 32 } +} - trait B { - type Ty: A; - } +trait B { + type Ty: A; +} - impl B for u16 { - type Ty = X; - } +impl B for u16 { + type Ty = X; +} - fn ttt() { - let inp = Y; - x::(&inp); - //^^^^ expected &X, got &Y - } - "#, +fn ttt() { + let inp = Y; + x::(&inp); + //^^^^ expected &X, got &Y +} +"#, ); } diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 3c7cfbaed3a40..467b94a2662a1 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -48,7 +48,7 @@ pub struct TraitEnvironment { pub krate: CrateId, pub block: Option, // FIXME make this a BTreeMap - pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, + pub(crate) traits_from_clauses: Box<[(Ty, TraitId)]>, pub env: chalk_ir::Environment, } @@ -57,7 +57,7 @@ impl TraitEnvironment { TraitEnvironment { krate, block: None, - traits_from_clauses: Vec::new(), + traits_from_clauses: Box::default(), env: chalk_ir::Environment::new(Interner), } } diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 09ab60dd549f4..4c1dfbc294e5b 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -13,9 +13,9 @@ doctest = false [dependencies] rustc-hash = "1.1.0" -either = "1.7.0" +either.workspace = true arrayvec = "0.7.2" -itertools = "0.10.5" +itertools.workspace = true smallvec.workspace = true triomphe.workspace = true once_cell = "1.17.1" diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 67d3169243c6f..cf9a2b73d9b1f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -3,7 +3,7 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. -pub use hir_ty::diagnostics::{CaseType, IncoherentImpl, IncorrectCase}; +pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -53,6 +53,9 @@ diagnostics![ PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, + TraitImplIncorrectSafety, + TraitImplMissingAssocItems, + TraitImplOrphan, TypedHole, TypeMismatch, UndeclaredLabel, @@ -280,3 +283,30 @@ pub struct MovedOutOfRef { pub ty: Type, pub span: InFile, } + +#[derive(Debug, PartialEq, Eq)] +pub struct IncoherentImpl { + pub file_id: HirFileId, + pub impl_: AstPtr, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplOrphan { + pub file_id: HirFileId, + pub impl_: AstPtr, +} + +// FIXME: Split this off into the corresponding 4 rustc errors +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplIncorrectSafety { + pub file_id: HirFileId, + pub impl_: AstPtr, + pub should_be_safe: bool, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplMissingAssocItems { + pub file_id: HirFileId, + pub impl_: AstPtr, + pub missing: Vec<(Name, AssocItem)>, +} diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index ac171026d5d7f..5847c8a9fb52b 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -1,6 +1,6 @@ //! HirDisplay implementations for various hir types. use hir_def::{ - data::adt::VariantData, + data::adt::{StructKind, VariantData}, generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, @@ -163,7 +163,40 @@ impl HirDisplay for Struct { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; + + let variant_data = self.variant_data(f.db); + if let StructKind::Tuple = variant_data.kind() { + f.write_char('(')?; + let mut it = variant_data.fields().iter().peekable(); + + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; + field.ty(f.db).hir_fmt(f)?; + if it.peek().is_some() { + f.write_str(", ")?; + } + } + + f.write_str(");")?; + } + write_where_clause(def_id, f)?; + + if let StructKind::Record = variant_data.kind() { + let fields = self.fields(f.db); + if fields.is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" {\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + } + Ok(()) } } @@ -176,6 +209,18 @@ impl HirDisplay for Enum { let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; + + let variants = self.variants(f.db); + if !variants.is_empty() { + f.write_str(" {\n")?; + for variant in variants { + f.write_str(" ")?; + variant.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + Ok(()) } } @@ -188,6 +233,18 @@ impl HirDisplay for Union { let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; + + let fields = self.fields(f.db); + if !fields.is_empty() { + f.write_str(" {\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + Ok(()) } } @@ -559,7 +616,7 @@ impl HirDisplay for TypeAlias { write_where_clause(def_id, f)?; if !data.bounds.is_empty() { f.write_str(": ")?; - f.write_joined(&data.bounds, " + ")?; + f.write_joined(data.bounds.iter(), " + ")?; } if let Some(ty) = &data.type_ref { f.write_str(" = ")?; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 93859611668ed..bdc11aa3561b7 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -34,7 +34,7 @@ pub mod symbols; mod display; -use std::{iter, ops::ControlFlow}; +use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; @@ -54,14 +54,14 @@ use hir_def::{ resolver::{HasResolver, Resolver}, src::HasSource as _, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, - EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId, - InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, - MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, + ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, + Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, + TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{name::name, MacroCallKind}; use hir_ty::{ - all_super_traits, autoderef, + all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, known_const_to_ast, @@ -90,17 +90,7 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{resolve_doc_path_on, HasAttrs}, - diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, - IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, - MacroExpansionParseError, MalformedDerive, MismatchedArgCount, - MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, - MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, - UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, - UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, - UnresolvedProcMacro, UnusedMut, UnusedVariable, - }, + diagnostics::*, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, }; @@ -604,6 +594,7 @@ impl Module { let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); + let mut impl_assoc_items_scratch = vec![]; for impl_def in self.impl_defs(db) { let loc = impl_def.id.lookup(db.upcast()); let tree = loc.id.item_tree(db.upcast()); @@ -614,19 +605,109 @@ impl Module { // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; } + let ast_id_map = db.ast_id_map(file_id); for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { emit_def_diagnostic(db, acc, diag); } if inherent_impls.invalid_impls().contains(&impl_def.id) { - let ast_id_map = db.ast_id_map(file_id); - acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) } - for item in impl_def.items(db) { - let def: DefWithBody = match item { + if !impl_def.check_orphan_rules(db) { + acc.push(TraitImplOrphan { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) + } + + let trait_ = impl_def.trait_(db); + let trait_is_unsafe = trait_.map_or(false, |t| t.is_unsafe(db)); + let impl_is_negative = impl_def.is_negative(db); + let impl_is_unsafe = impl_def.is_unsafe(db); + + let drop_maybe_dangle = (|| { + // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper + let trait_ = trait_?; + let drop_trait = db.lang_item(self.krate().into(), LangItem::Drop)?.as_trait()?; + if drop_trait != trait_.into() { + return None; + } + let parent = impl_def.id.into(); + let generic_params = db.generic_params(parent); + let lifetime_params = generic_params.lifetimes.iter().map(|(local_id, _)| { + GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }) + }); + let type_params = generic_params + .iter() + .filter(|(_, it)| it.type_param().is_some()) + .map(|(local_id, _)| { + GenericParamId::TypeParamId(TypeParamId::from_unchecked( + TypeOrConstParamId { parent, local_id }, + )) + }); + let res = type_params + .chain(lifetime_params) + .any(|p| db.attrs(AttrDefId::GenericParamId(p)).by_key("may_dangle").exists()); + Some(res) + })() + .unwrap_or(false); + + match (impl_is_unsafe, trait_is_unsafe, impl_is_negative, drop_maybe_dangle) { + // unsafe negative impl + (true, _, true, _) | + // unsafe impl for safe trait + (true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: true }.into()), + // safe impl for unsafe trait + (false, true, false, _) | + // safe impl of dangling drop + (false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: false }.into()), + _ => (), + }; + + if let Some(trait_) = trait_ { + let items = &db.trait_data(trait_.into()).items; + let required_items = items.iter().filter(|&(_, assoc)| match *assoc { + AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), + AssocItemId::ConstId(_) => true, + AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), + }); + impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().map( + |&item| { + ( + item, + match item { + AssocItemId::FunctionId(it) => db.function_data(it).name.clone(), + AssocItemId::ConstId(it) => { + db.const_data(it).name.as_ref().unwrap().clone() + } + AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(), + }, + ) + }, + )); + + let missing: Vec<_> = required_items + .filter(|(name, id)| { + !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| { + discriminant(impl_item) == discriminant(id) && impl_name == name + }) + }) + .map(|(name, item)| (name.clone(), AssocItem::from(*item))) + .collect(); + if !missing.is_empty() { + acc.push( + TraitImplMissingAssocItems { + impl_: ast_id_map.get(node.ast_id()), + file_id, + missing, + } + .into(), + ) + } + impl_assoc_items_scratch.clear(); + } + + for &item in &db.impl_data(impl_def.id).items { + let def: DefWithBody = match AssocItem::from(item) { AssocItem::Function(it) => it.into(), AssocItem::Const(it) => it.into(), AssocItem::TypeAlias(_) => continue, @@ -665,8 +746,15 @@ impl Module { db: &dyn DefDatabase, item: impl Into, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { - hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std) + hir_def::find_path::find_path( + db, + item.into().into(), + self.into(), + prefer_no_std, + prefer_prelude, + ) } /// Finds a path that can be used to refer to the given item from within @@ -677,6 +765,7 @@ impl Module { item: impl Into, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { hir_def::find_path::find_path_prefixed( db, @@ -684,6 +773,7 @@ impl Module { self.into(), prefix_kind, prefer_no_std, + prefer_prelude, ) } } @@ -1447,9 +1537,7 @@ impl DefWithBody { let (body, source_map) = db.body_with_source_map(self.into()); for (_, def_map) in body.blocks(db.upcast()) { - for diag in def_map.diagnostics() { - emit_def_diagnostic(db, acc, diag); - } + Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc); } for diag in source_map.diagnostics() { @@ -3390,6 +3478,10 @@ impl Impl { db.impl_data(self.id).is_negative } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + db.impl_data(self.id).is_unsafe + } + pub fn module(self, db: &dyn HirDatabase) -> Module { self.id.lookup(db.upcast()).container.into() } @@ -3398,6 +3490,10 @@ impl Impl { let src = self.source(db)?; src.file_id.as_builtin_derive_attr_node(db.upcast()) } + + pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool { + check_orphan_rules(db, self.id) + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml index 447e38f91f43e..a622ec1a9532e 100644 --- a/crates/ide-assists/Cargo.toml +++ b/crates/ide-assists/Cargo.toml @@ -14,8 +14,8 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" -either = "1.7.0" +itertools.workspace = true +either.workspace = true smallvec.workspace = true # local deps diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index b273ebc85a506..fbe17dbfd7714 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -14,5 +14,6 @@ pub struct AssistConfig { pub allowed: Option>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub assist_emit_must_use: bool, } diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index c0e5429a22c9b..410c623109ea0 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2245,6 +2245,37 @@ impl b::LocalTrait for B { fn no_skip_default_2() -> Option<()> { todo!() } +} + "#, + ) + } + + #[test] + fn doc_hidden_nondefault_member() { + check_assist( + add_missing_impl_members, + r#" +//- /lib.rs crate:b new_source_root:local +trait LocalTrait { + #[doc(hidden)] + fn no_skip_non_default() -> Option<()>; + + #[doc(hidden)] + fn skip_default() -> Option<()> { + todo!() + } +} + +//- /main.rs crate:a deps:b +struct B; +impl b::Loc$0alTrait for B {} + "#, + r#" +struct B; +impl b::LocalTrait for B { + fn no_skip_non_default() -> Option<()> { + ${0:todo!()} + } } "#, ) diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index c8b78b0941694..2374da9a343f4 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -88,7 +88,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)?, + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -140,7 +146,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) @@ -173,7 +185,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant.clone(), ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant.clone(), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) @@ -440,11 +458,16 @@ fn build_pat( module: hir::Module, var: ExtendedVariant, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { match var { ExtendedVariant::Variant(var) => { - let path = - mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var), prefer_no_std)?); + let path = mod_path_to_ast(&module.find_use_path( + db, + ModuleDef::from(var), + prefer_no_std, + prefer_prelude, + )?); // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though Some(match var.source(db)?.value.kind() { diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index 36f68d1767735..88fd0b1b7339b 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -1,6 +1,9 @@ +use either::Either; use ide_db::defs::{Definition, NameRefClass}; -use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxKind, T}; +use syntax::{ + ast::{self, make, HasArgList}, + ted, AstNode, +}; use crate::{ assist_context::{AssistContext, Assists}, @@ -25,21 +28,45 @@ use crate::{ // } // ``` pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| { - let arg_list = ctx.find_node_at_offset::()?; - if arg_list.args().next().is_some() { - return None; - } - cov_mark::hit!(add_turbo_fish_after_call); - cov_mark::hit!(add_type_ascription_after_call); - arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) - })?; - let next_token = ident.next_token()?; - if next_token.kind() == T![::] { + let turbofish_target = + ctx.find_node_at_offset::().map(Either::Left).or_else(|| { + let callable_expr = ctx.find_node_at_offset::()?; + + if callable_expr.arg_list()?.args().next().is_some() { + return None; + } + + cov_mark::hit!(add_turbo_fish_after_call); + cov_mark::hit!(add_type_ascription_after_call); + + match callable_expr { + ast::CallableExpr::Call(it) => { + let ast::Expr::PathExpr(path) = it.expr()? else { + return None; + }; + + Some(Either::Left(path.path()?.segment()?)) + } + ast::CallableExpr::MethodCall(it) => Some(Either::Right(it)), + } + })?; + + let already_has_turbofish = match &turbofish_target { + Either::Left(path_segment) => path_segment.generic_arg_list().is_some(), + Either::Right(method_call) => method_call.generic_arg_list().is_some(), + }; + + if already_has_turbofish { cov_mark::hit!(add_turbo_fish_one_fish_is_enough); return None; } - let name_ref = ast::NameRef::cast(ident.parent()?)?; + + let name_ref = match &turbofish_target { + Either::Left(path_segment) => path_segment.name_ref()?, + Either::Right(method_call) => method_call.name_ref()?, + }; + let ident = name_ref.ident_token()?; + let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { NameRefClass::Definition(def) => def, NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => { @@ -58,20 +85,27 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti if let Some(let_stmt) = ctx.find_node_at_offset::() { if let_stmt.colon_token().is_none() { - let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); - let semi_pos = let_stmt.syntax().last_token()?.text_range().end(); + if let_stmt.pat().is_none() { + return None; + } acc.add( AssistId("add_type_ascription", AssistKind::RefactorRewrite), "Add `: _` before assignment operator", ident.text_range(), - |builder| { + |edit| { + let let_stmt = edit.make_mut(let_stmt); + if let_stmt.semicolon_token().is_none() { - builder.insert(semi_pos, ";"); + ted::append_child(let_stmt.syntax(), make::tokens::semicolon()); } - match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), - None => builder.insert(type_pos, ": _"), + + let placeholder_ty = make::ty_placeholder().clone_for_update(); + + let_stmt.set_ty(Some(placeholder_ty.clone())); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, placeholder_ty); } }, )? @@ -91,38 +125,46 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti AssistId("add_turbo_fish", AssistKind::RefactorRewrite), "Add `::<>`", ident.text_range(), - |builder| { - builder.trigger_signature_help(); - match ctx.config.snippet_cap { - Some(cap) => { - let fish_head = get_snippet_fish_head(number_of_arguments); - let snip = format!("::<{fish_head}>"); - builder.insert_snippet(cap, ident.text_range().end(), snip) + |edit| { + edit.trigger_signature_help(); + + let new_arg_list = match turbofish_target { + Either::Left(path_segment) => { + edit.make_mut(path_segment).get_or_create_generic_arg_list() + } + Either::Right(method_call) => { + edit.make_mut(method_call).get_or_create_generic_arg_list() } - None => { - let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", "); - let snip = format!("::<{fish_head}>"); - builder.insert(ident.text_range().end(), snip); + }; + + let fish_head = get_fish_head(number_of_arguments).clone_for_update(); + + // Note: we need to replace the `new_arg_list` instead of being able to use something like + // `GenericArgList::add_generic_arg` as `PathSegment::get_or_create_generic_arg_list` + // always creates a non-turbofish form generic arg list. + ted::replace(new_arg_list.syntax(), fish_head.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + for arg in fish_head.generic_args() { + edit.add_placeholder_snippet(cap, arg) } } }, ) } -/// This will create a snippet string with tabstops marked -fn get_snippet_fish_head(number_of_arguments: usize) -> String { - let mut fish_head = (1..number_of_arguments) - .format_with("", |i, f| f(&format_args!("${{{i}:_}}, "))) - .to_string(); - - // tabstop 0 is a special case and always the last one - fish_head.push_str("${0:_}"); - fish_head +/// This will create a turbofish generic arg list corresponding to the number of arguments +fn get_fish_head(number_of_arguments: usize) -> ast::GenericArgList { + let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into()); + make::turbofish_generic_arg_list(args) } #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; + use crate::tests::{ + check_assist, check_assist_by_label, check_assist_not_applicable, + check_assist_not_applicable_by_label, + }; use super::*; @@ -363,6 +405,20 @@ fn main() { ); } + #[test] + fn add_type_ascription_missing_pattern() { + check_assist_not_applicable_by_label( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let = make$0() +} +"#, + "Add `: _` before assignment operator", + ); + } + #[test] fn add_turbo_fish_function_lifetime_parameter() { check_assist( diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index cafd57a977271..f508c42c53e4b 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -93,6 +93,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< &ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_no_std, ); if proposed_imports.is_empty() { return None; diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index 082839118c586..11facc5bee2ac 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -348,6 +348,7 @@ fn augment_references_with_imports( ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .map(|mod_path| { make::path_concat(mod_path_to_ast(&mod_path), make::path_from_text("Bool")) diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index 872b52c98fff2..d649f13d6ee35 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -50,7 +50,12 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - _ => return None, }; - mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?) + mod_path_to_ast(&module.find_use_path( + ctx.db(), + src_type_def, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?) }; let dest_type = match &ast_trait { diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 32db5ee8dabf8..1f3caa7db33f1 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -205,6 +205,7 @@ fn augment_references_with_imports( ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .map(|mod_path| { make::path_concat( diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index f30ca2552d368..65b497e83aa2a 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -3,10 +3,12 @@ use ide_db::{ defs::Definition, search::{FileReference, SearchScope, UsageSearchResult}, }; +use itertools::Itertools; use syntax::{ - ast::{self, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr}, - TextRange, + ast::{self, make, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr}, + ted, T, }; +use text_edit::TextRange; use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder}; @@ -61,27 +63,36 @@ pub(crate) fn destructure_tuple_binding_impl( acc.add( AssistId("destructure_tuple_binding_in_sub_pattern", AssistKind::RefactorRewrite), "Destructure tuple in sub-pattern", - data.range, - |builder| { - edit_tuple_assignment(ctx, builder, &data, true); - edit_tuple_usages(&data, builder, ctx, true); - }, + data.ident_pat.syntax().text_range(), + |edit| destructure_tuple_edit_impl(ctx, edit, &data, true), ); } acc.add( AssistId("destructure_tuple_binding", AssistKind::RefactorRewrite), if with_sub_pattern { "Destructure tuple in place" } else { "Destructure tuple" }, - data.range, - |builder| { - edit_tuple_assignment(ctx, builder, &data, false); - edit_tuple_usages(&data, builder, ctx, false); - }, + data.ident_pat.syntax().text_range(), + |edit| destructure_tuple_edit_impl(ctx, edit, &data, false), ); Some(()) } +fn destructure_tuple_edit_impl( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + data: &TupleData, + in_sub_pattern: bool, +) { + let assignment_edit = edit_tuple_assignment(ctx, edit, &data, in_sub_pattern); + let current_file_usages_edit = edit_tuple_usages(&data, edit, ctx, in_sub_pattern); + + assignment_edit.apply(); + if let Some(usages_edit) = current_file_usages_edit { + usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit)) + } +} + fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option { if ident_pat.at_token().is_some() { // Cannot destructure pattern with sub-pattern: @@ -109,7 +120,6 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option) -> Option>(); - Some(TupleData { ident_pat, range, ref_type, field_names, usages }) + Some(TupleData { ident_pat, ref_type, field_names, usages }) } fn generate_name( @@ -142,72 +152,100 @@ enum RefType { } struct TupleData { ident_pat: IdentPat, - // name: String, - range: TextRange, ref_type: Option, field_names: Vec, - // field_types: Vec, usages: Option, } fn edit_tuple_assignment( ctx: &AssistContext<'_>, - builder: &mut SourceChangeBuilder, + edit: &mut SourceChangeBuilder, data: &TupleData, in_sub_pattern: bool, -) { +) -> AssignmentEdit { + let ident_pat = edit.make_mut(data.ident_pat.clone()); + let tuple_pat = { let original = &data.ident_pat; let is_ref = original.ref_token().is_some(); let is_mut = original.mut_token().is_some(); - let fields = data.field_names.iter().map(|name| { - ast::Pat::from(ast::make::ident_pat(is_ref, is_mut, ast::make::name(name))) - }); - ast::make::tuple_pat(fields) + let fields = data + .field_names + .iter() + .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name)))); + make::tuple_pat(fields).clone_for_update() }; - let add_cursor = |text: &str| { - // place cursor on first tuple item - let first_tuple = &data.field_names[0]; - text.replacen(first_tuple, &format!("$0{first_tuple}"), 1) - }; + if let Some(cap) = ctx.config.snippet_cap { + // place cursor on first tuple name + if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() { + edit.add_tabstop_before( + cap, + first_pat.name().expect("first ident pattern should have a name"), + ) + } + } - // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` - if in_sub_pattern { - let text = format!(" @ {tuple_pat}"); - match ctx.config.snippet_cap { - Some(cap) => { - let snip = add_cursor(&text); - builder.insert_snippet(cap, data.range.end(), snip); - } - None => builder.insert(data.range.end(), text), - }; - } else { - let text = tuple_pat.to_string(); - match ctx.config.snippet_cap { - Some(cap) => { - let snip = add_cursor(&text); - builder.replace_snippet(cap, data.range, snip); - } - None => builder.replace(data.range, text), - }; + AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern } +} +struct AssignmentEdit { + ident_pat: ast::IdentPat, + tuple_pat: ast::TuplePat, + in_sub_pattern: bool, +} + +impl AssignmentEdit { + fn apply(self) { + // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` + if self.in_sub_pattern { + self.ident_pat.set_pat(Some(self.tuple_pat.into())) + } else { + ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) + } } } fn edit_tuple_usages( data: &TupleData, - builder: &mut SourceChangeBuilder, + edit: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, in_sub_pattern: bool, -) { +) -> Option> { + let mut current_file_usages = None; + if let Some(usages) = data.usages.as_ref() { - for (file_id, refs) in usages.iter() { - builder.edit_file(*file_id); + // We need to collect edits first before actually applying them + // as mapping nodes to their mutable node versions requires an + // unmodified syntax tree. + // + // We also defer editing usages in the current file first since + // tree mutation in the same file breaks when `builder.edit_file` + // is called + + if let Some((_, refs)) = usages.iter().find(|(file_id, _)| **file_id == ctx.file_id()) { + current_file_usages = Some( + refs.iter() + .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) + .collect_vec(), + ); + } - for r in refs { - edit_tuple_usage(ctx, builder, r, data, in_sub_pattern); + for (file_id, refs) in usages.iter() { + if *file_id == ctx.file_id() { + continue; } + + edit.edit_file(*file_id); + + let tuple_edits = refs + .iter() + .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) + .collect_vec(); + + tuple_edits.into_iter().for_each(|tuple_edit| tuple_edit.apply(edit)) } } + + current_file_usages } fn edit_tuple_usage( ctx: &AssistContext<'_>, @@ -215,25 +253,14 @@ fn edit_tuple_usage( usage: &FileReference, data: &TupleData, in_sub_pattern: bool, -) { +) -> Option { match detect_tuple_index(usage, data) { - Some(index) => edit_tuple_field_usage(ctx, builder, data, index), - None => { - if in_sub_pattern { - cov_mark::hit!(destructure_tuple_call_with_subpattern); - return; - } - - // no index access -> make invalid -> requires handling by user - // -> put usage in block comment - // - // Note: For macro invocations this might result in still valid code: - // When a macro accepts the tuple as argument, as well as no arguments at all, - // uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`). - // But this is an unlikely case. Usually the resulting macro call will become erroneous. - builder.insert(usage.range.start(), "/*"); - builder.insert(usage.range.end(), "*/"); + Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)), + None if in_sub_pattern => { + cov_mark::hit!(destructure_tuple_call_with_subpattern); + return None; } + None => Some(EditTupleUsage::NoIndex(usage.range)), } } @@ -242,19 +269,47 @@ fn edit_tuple_field_usage( builder: &mut SourceChangeBuilder, data: &TupleData, index: TupleIndex, -) { +) -> EditTupleUsage { let field_name = &data.field_names[index.index]; + let field_name = make::expr_path(make::ext::ident_path(field_name)); if data.ref_type.is_some() { - let ref_data = handle_ref_field_usage(ctx, &index.field_expr); - builder.replace(ref_data.range, ref_data.format(field_name)); + let (replace_expr, ref_data) = handle_ref_field_usage(ctx, &index.field_expr); + let replace_expr = builder.make_mut(replace_expr); + EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name)) } else { - builder.replace(index.range, field_name); + let field_expr = builder.make_mut(index.field_expr); + EditTupleUsage::ReplaceExpr(field_expr.into(), field_name) + } +} +enum EditTupleUsage { + /// no index access -> make invalid -> requires handling by user + /// -> put usage in block comment + /// + /// Note: For macro invocations this might result in still valid code: + /// When a macro accepts the tuple as argument, as well as no arguments at all, + /// uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`). + /// But this is an unlikely case. Usually the resulting macro call will become erroneous. + NoIndex(TextRange), + ReplaceExpr(ast::Expr, ast::Expr), +} + +impl EditTupleUsage { + fn apply(self, edit: &mut SourceChangeBuilder) { + match self { + EditTupleUsage::NoIndex(range) => { + edit.insert(range.start(), "/*"); + edit.insert(range.end(), "*/"); + } + EditTupleUsage::ReplaceExpr(target_expr, replace_with) => { + ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax()) + } + } } } + struct TupleIndex { index: usize, - range: TextRange, field_expr: FieldExpr, } fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option { @@ -296,7 +351,7 @@ fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option Option String { - match (self.needs_deref, self.needs_parentheses) { - (true, true) => format!("(*{field_name})"), - (true, false) => format!("*{field_name}"), - (false, true) => format!("({field_name})"), - (false, false) => field_name.to_string(), + fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr { + if self.needs_deref { + expr = make::expr_prefix(T![*], expr); } + + if self.needs_parentheses { + expr = make::expr_paren(expr); + } + + return expr; } } -fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> RefData { +fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> (ast::Expr, RefData) { let s = field_expr.syntax(); - let mut ref_data = - RefData { range: s.text_range(), needs_deref: true, needs_parentheses: true }; + let mut ref_data = RefData { needs_deref: true, needs_parentheses: true }; + let mut target_node = field_expr.clone().into(); let parent = match s.parent().map(ast::Expr::cast) { Some(Some(parent)) => parent, Some(None) => { ref_data.needs_parentheses = false; - return ref_data; + return (target_node, ref_data); } - None => return ref_data, + None => return (target_node, ref_data), }; match parent { @@ -342,7 +399,7 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re // there might be a ref outside: `&(t.0)` -> can be removed if let Some(it) = it.syntax().parent().and_then(ast::RefExpr::cast) { ref_data.needs_deref = false; - ref_data.range = it.syntax().text_range(); + target_node = it.into(); } } ast::Expr::RefExpr(it) => { @@ -351,8 +408,8 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re ref_data.needs_parentheses = false; // might be surrounded by parens -> can be removed too match it.syntax().parent().and_then(ast::ParenExpr::cast) { - Some(parent) => ref_data.range = parent.syntax().text_range(), - None => ref_data.range = it.syntax().text_range(), + Some(parent) => target_node = parent.into(), + None => target_node = it.into(), }; } // higher precedence than deref `*` @@ -414,7 +471,7 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re } }; - ref_data + (target_node, ref_data) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index de591cfde9499..6b48d15881523 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -163,6 +163,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mod_path) = mod_path { diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index e4f64ccc754e0..37db27a8fc022 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -384,6 +384,7 @@ fn process_references( *enum_module_def, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs index 81545396175ba..473c699b595c6 100644 --- a/crates/ide-assists/src/handlers/generate_deref.rs +++ b/crates/ide-assists/src/handlers/generate_deref.rs @@ -58,8 +58,12 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let field_name = field.name()?; @@ -99,8 +103,12 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let target = field.syntax().text_range(); diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs index 824255e4f8e32..7bfd599660db1 100644 --- a/crates/ide-assists/src/handlers/generate_new.rs +++ b/crates/ide-assists/src/handlers/generate_new.rs @@ -67,6 +67,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let expr = use_trivial_constructor( diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index 4bf974a5655af..ff65aac82e070 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -48,6 +48,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index 239149dc411d6..fde46db3058e6 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -37,8 +37,11 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; - let mut proposed_imports = - import_assets.search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std); + let mut proposed_imports = import_assets.search_for_relative_paths( + &ctx.sema, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ); if proposed_imports.is_empty() { return None; } diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index ac45581b7b440..69a4e748b7c5d 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -82,7 +82,12 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std) + .find_use_path( + ctx.sema.db, + hir::ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .as_ref() .map(mod_path_to_ast) .zip(Some(trait_)) diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index dbbc56958f1e2..f03eb6118a524 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -68,6 +68,7 @@ pub(crate) fn replace_qualified_name_with_use( module, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) }) .flatten(); diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index cc3e251a8f2a6..25b3d6d9da91c 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; @@ -44,6 +45,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; @@ -98,6 +100,11 @@ pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { check(assist, ra_fixture, ExpectedResult::NotApplicable, None); } +#[track_caller] +pub(crate) fn check_assist_not_applicable_by_label(assist: Handler, ra_fixture: &str, label: &str) { + check(assist, ra_fixture, ExpectedResult::NotApplicable, Some(label)); +} + /// Check assist in unresolved state. Useful to check assists for lazy computation. #[track_caller] pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) { diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index a262570d94e7d..f51e99a914e2b 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -106,8 +106,18 @@ pub fn filter_assoc_items( .iter() .copied() .filter(|assoc_item| { - !(ignore_items == IgnoreAssocItems::DocHiddenAttrPresent - && assoc_item.attrs(sema.db).has_doc_hidden()) + if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent + && assoc_item.attrs(sema.db).has_doc_hidden() + { + if let hir::AssocItem::Function(f) = assoc_item { + if !f.has_body(sema.db) { + return true; + } + } + return false; + } + + return true; }) // Note: This throws away items with no source. .filter_map(|assoc_item| { diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml index 092fb303668fe..60f90a41b962f 100644 --- a/crates/ide-completion/Cargo.toml +++ b/crates/ide-completion/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true once_cell = "1.17.0" smallvec.workspace = true diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index f60ac150164c4..7d38c638a8ed5 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -626,6 +626,7 @@ fn enum_variants_with_paths( ctx.db, hir::ModuleDef::from(variant), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 9daa6984c3e38..d3c817d4b43ad 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -175,6 +175,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(strukt), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); @@ -197,6 +198,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(un), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 0961021e48e07..d74d3b264ae5f 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,12 @@ fn import_on_the_fly( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -299,7 +304,12 @@ fn import_on_the_fly_pat_( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -336,7 +346,12 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(|import| { !ctx.is_item_hidden(&import.item_to_import) diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 3d025f284bbb7..ed5ddde8fbfe9 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub snippet_cap: Option, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub snippets: Vec, pub limit: Option, } diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 2fad293d16d7d..aaf7cd7843afe 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -263,6 +263,7 @@ pub fn resolve_completion_edits( candidate, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) }) .find(|mod_path| mod_path.display(db).to_string() == full_import_path); diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 343719c53694e..50618296ee437 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -179,6 +179,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option 1).then(|| LocatedImport::new(path.clone(), item, item, None))) }; diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 284bdd8af21f8..9db8e972dd4ba 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -68,6 +68,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), prefer_no_std: false, + prefer_prelude: true, insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, prefix_kind: PrefixKind::Plain, diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml index faec742068117..4a2e770f193a0 100644 --- a/crates/ide-db/Cargo.toml +++ b/crates/ide-db/Cargo.toml @@ -13,16 +13,16 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -tracing = "0.1.35" -rayon = "1.6.1" +tracing.workspace = true +rayon.workspace = true fst = { version = "0.4.7", default-features = false } rustc-hash = "1.1.0" once_cell = "1.17.0" -either = "1.7.0" -itertools = "0.10.5" +either.workspace = true +itertools.workspace = true arrayvec = "0.7.2" -indexmap = "2.0.0" -memchr = "2.5.0" +indexmap.workspace = true +memchr = "2.6.4" triomphe.workspace = true nohash-hasher.workspace = true @@ -43,7 +43,7 @@ line-index.workspace = true [dev-dependencies] expect-test = "1.4.0" oorandom = "11.1.3" -xshell = "0.2.2" +xshell.workspace = true # local deps test-utils.workspace = true diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index da5a951f0b7b3..04263d15d0a5a 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -220,9 +220,10 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { let _p = profile::span("import_assets::search_for_imports"); - self.search_for(sema, Some(prefix_kind), prefer_no_std) + self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) } /// This may return non-absolute paths if a part of the returned path is already imported into scope. @@ -230,9 +231,10 @@ impl ImportAssets { &self, sema: &Semantics<'_, RootDatabase>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { let _p = profile::span("import_assets::search_for_relative_paths"); - self.search_for(sema, None, prefer_no_std) + self.search_for(sema, None, prefer_no_std, prefer_prelude) } /// Requires imports to by prefix instead of fuzzily. @@ -270,6 +272,7 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { let _p = profile::span("import_assets::search_for"); @@ -281,6 +284,7 @@ impl ImportAssets { &self.module_with_candidate, prefixed, prefer_no_std, + prefer_prelude, ) }; @@ -594,11 +598,18 @@ fn get_mod_path( module_with_candidate: &Module, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { if let Some(prefix_kind) = prefixed { - module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind, prefer_no_std) + module_with_candidate.find_use_path_prefixed( + db, + item_to_search, + prefix_kind, + prefer_no_std, + prefer_prelude, + ) } else { - module_with_candidate.find_use_path(db, item_to_search, prefer_no_std) + module_with_candidate.find_use_path(db, item_to_search, prefer_no_std, prefer_prelude) } } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index fb75b5b458482..fa9339f30f206 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -277,6 +277,7 @@ impl Ctx<'_> { self.source_scope.db.upcast(), hir::ModuleDef::Trait(trait_ref), false, + true, )?; match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), @@ -311,8 +312,12 @@ impl Ctx<'_> { } } - let found_path = - self.target_module.find_use_path(self.source_scope.db.upcast(), def, false)?; + let found_path = self.target_module.find_use_path( + self.source_scope.db.upcast(), + def, + false, + true, + )?; let res = mod_path_to_ast(&found_path).clone_for_update(); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { if let Some(segment) = res.segment() { diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 9c4f0ac8c9fc4..22438a203bd78 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -584,7 +584,7 @@ impl<'a> FindUsages<'a> { ) -> bool { match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(Definition::SelfType(impl_))) - if impl_.self_ty(self.sema.db) == *self_ty => + if impl_.self_ty(self.sema.db).as_adt() == self_ty.as_adt() => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 39763479c65a2..c7188f1f7943b 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -140,10 +140,10 @@ impl SnippetEdit { .with_position() .map(|pos| { let (snippet, index) = match pos { - itertools::Position::First(it) | itertools::Position::Middle(it) => it, + (itertools::Position::First, it) | (itertools::Position::Middle, it) => it, // last/only snippet gets index 0 - itertools::Position::Last((snippet, _)) - | itertools::Position::Only((snippet, _)) => (snippet, 0), + (itertools::Position::Last, (snippet, _)) + | (itertools::Position::Only, (snippet, _)) => (snippet, 0), }; let range = match snippet { diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index 14aa3940199ab..f4055024cc32c 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -13,9 +13,9 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -either = "1.7.0" -itertools = "0.10.5" -serde_json = "1.0.86" +either.workspace = true +itertools.workspace = true +serde_json.workspace = true once_cell = "1.17.0" # local deps diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 85dbb7e6f2637..0f12e814ba39d 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -599,12 +599,12 @@ fn main() { //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` fn BAZ() { //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` - let INNER_INNER = 42; - //^^^^^^^^^^^ 💡 warn: Variable `INNER_INNER` should have snake_case name, e.g. `inner_inner` + let _INNER_INNER = 42; + //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_INNER` should have snake_case name, e.g. `_inner_inner` } - let INNER_LOCAL = 42; - //^^^^^^^^^^^ 💡 warn: Variable `INNER_LOCAL` should have snake_case name, e.g. `inner_local` + let _INNER_LOCAL = 42; + //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_LOCAL` should have snake_case name, e.g. `_inner_local` } } "#, diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index a337e2660d6a6..659b74445f8fe 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -136,6 +136,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } @@ -148,6 +149,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index c09be3fee7e15..d7dca1083a076 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -122,6 +122,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option *mut A { &mut *a }; + //^^^^^ 💡 warn: variable does not need to be mutable + let _ = b(); +} "#, ); } diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs new file mode 100644 index 0000000000000..251a645292ed5 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -0,0 +1,129 @@ +use hir::InFile; +use syntax::ast; + +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; + +// Diagnostic: trait-impl-incorrect-safety +// +// Diagnoses incorrect safety annotations of trait impls. +pub(crate) fn trait_impl_incorrect_safety( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplIncorrectSafety, +) -> Diagnostic { + Diagnostic::new( + DiagnosticCode::Ra("trait-impl-incorrect-safety", Severity::Error), + if d.should_be_safe { + "unsafe impl for safe trait" + } else { + "impl for unsafe trait needs to be unsafe" + }, + adjusted_display_range::( + ctx, + InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() }, + &|impl_| { + if d.should_be_safe { + Some(match (impl_.unsafe_token(), impl_.impl_token()) { + (None, None) => return None, + (None, Some(t)) | (Some(t), None) => t.text_range(), + (Some(t1), Some(t2)) => t1.text_range().cover(t2.text_range()), + }) + } else { + impl_.impl_token().map(|t| t.text_range()) + } + }, + ), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +trait Safe {} +unsafe trait Unsafe {} + + impl Safe for () {} + + impl Unsafe for () {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl Safe for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl Unsafe for () {} +"#, + ); + } + + #[test] + fn drop_may_dangle() { + check_diagnostics( + r#" +#[lang = "drop"] +trait Drop {} +struct S; +struct L<'l>; + + impl Drop for S {} + + impl<#[may_dangle] T> Drop for S {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl Drop for S {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl<#[may_dangle] T> Drop for S {} + + impl<'l> Drop for L<'l> {} + + impl<#[may_dangle] 'l> Drop for L<'l> {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl<'l> Drop for L<'l> {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl<#[may_dangle] 'l> Drop for L<'l> {} +"#, + ); + } + + #[test] + fn negative() { + check_diagnostics( + r#" +trait Trait {} + + impl !Trait for () {} + + unsafe impl !Trait for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + +unsafe trait UnsafeTrait {} + + impl !UnsafeTrait for () {} + + unsafe impl !UnsafeTrait for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + +"#, + ); + } + + #[test] + fn inherent() { + check_diagnostics( + r#" +struct S; + + impl S {} + + unsafe impl S {} +//^^^^^^^^^^^ error: unsafe impl for safe trait +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs new file mode 100644 index 0000000000000..40d0b6fdd4b0e --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -0,0 +1,102 @@ +use hir::InFile; +use itertools::Itertools; +use syntax::{ast, AstNode}; + +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: trait-impl-missing-assoc_item +// +// Diagnoses missing trait items in a trait impl. +pub(crate) fn trait_impl_missing_assoc_item( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplMissingAssocItems, +) -> Diagnostic { + let missing = d.missing.iter().format_with(", ", |(name, item), f| { + f(&match *item { + hir::AssocItem::Function(_) => "`fn ", + hir::AssocItem::Const(_) => "`const ", + hir::AssocItem::TypeAlias(_) => "`type ", + })?; + f(&name.display(ctx.sema.db))?; + f(&"`") + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0046"), + format!("not all trait items implemented, missing: {missing}"), + adjusted_display_range::( + ctx, + InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() }, + &|impl_| impl_.trait_().map(|t| t.syntax().text_range()), + ), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +trait Trait { + const C: (); + type T; + fn f(); +} + +impl Trait for () { + const C: () = (); + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C`, `type T`, `fn f` +} + +"#, + ); + } + + #[test] + fn default() { + check_diagnostics( + r#" +trait Trait { + const C: (); + type T = (); + fn f() {} +} + +impl Trait for () { + const C: () = (); + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + } + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` +} + +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs new file mode 100644 index 0000000000000..159d87d269de0 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs @@ -0,0 +1,106 @@ +use hir::InFile; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: trait-impl-orphan +// +// Only traits defined in the current crate can be implemented for arbitrary types +pub(crate) fn trait_impl_orphan( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplOrphan, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0117"), + format!("only traits defined in the current crate can be implemented for arbitrary types"), + InFile::new(d.file_id, d.impl_.clone().into()), + ) + // Not yet checked for false positives + .experimental() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar; +//- /main.rs crate:main deps:foo,bar +struct LocalType; +trait LocalTrait {} + impl foo::Foo for bar::Bar {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types +impl foo::Foo for LocalType {} +impl LocalTrait for bar::Bar {} +"#, + ); + } + + #[test] + fn generics() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar(T); +//- /main.rs crate:main deps:foo,bar +struct LocalType; +trait LocalTrait {} + impl foo::Foo for bar::Bar {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + + impl foo::Foo for bar::Bar> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + + impl foo::Foo> for bar::Bar {} + + impl foo::Foo>> for bar::Bar> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types +"#, + ); + } + + #[test] + fn fundamental() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar(T); +#[lang = "owned_box"] +#[fundamental] +pub struct Box(T); +//- /main.rs crate:main deps:foo,bar +struct LocalType; + impl foo::Foo for bar::Box {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + impl foo::Foo for &LocalType {} + impl foo::Foo for bar::Box {} +"#, + ); + } + + #[test] + fn dyn_object() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar; +//- /main.rs crate:main deps:foo,bar +trait LocalTrait {} +impl foo::Foo for dyn LocalTrait {} +impl foo::Foo for Bar {} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 14454fe8dc40e..c92d92ceae8c8 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -278,6 +278,7 @@ struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { @@ -290,6 +291,7 @@ struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { @@ -737,6 +739,21 @@ fn f() -> i32 { 0 } fn g() { return; } +"#, + ); + } + + #[test] + fn smoke_test_inner_items() { + check_diagnostics( + r#" +fn f() { + fn inner() -> i32 { + return; + // ^^^^^^ error: expected i32, found () + 0 + } +} "#, ); } diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index fe5567544e834..6744895f3cd2e 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -44,6 +44,9 @@ mod handlers { pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod trait_impl_orphan; + pub(crate) mod trait_impl_incorrect_safety; + pub(crate) mod trait_impl_missing_assoc_item; pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; @@ -225,6 +228,7 @@ pub struct DiagnosticsConfig { // FIXME: We may want to include a whole `AssistConfig` here pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, } impl DiagnosticsConfig { @@ -247,6 +251,7 @@ impl DiagnosticsConfig { skip_glob_imports: false, }, prefer_no_std: false, + prefer_prelude: true, } } } @@ -356,6 +361,9 @@ pub fn diagnostics( AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), + AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d), + AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d), + AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d), AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index ee0e0354906ed..c766a018bfd05 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -5,7 +5,7 @@ use expect_test::Expect; use ide_db::{ assists::AssistResolveStrategy, base_db::{fixture::WithFixture, SourceDatabaseExt}, - RootDatabase, + LineIndexDatabase, RootDatabase, }; use stdx::trim_indent; use test_utils::{assert_eq_text, extract_annotations, MiniCore}; @@ -43,7 +43,8 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) .pop() .expect("no diagnostics"); - let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth]; + let fix = + &diagnostic.fixes.expect(&format!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); @@ -103,6 +104,7 @@ pub(crate) fn check_diagnostics(ra_fixture: &str) { pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) { let (db, files) = RootDatabase::with_many_files(ra_fixture); for file_id in files { + let line_index = db.line_index(file_id); let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); let expected = extract_annotations(&db.file_text(file_id)); @@ -136,8 +138,16 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur } } if expected != actual { - let fneg = expected.iter().filter(|x| !actual.contains(x)).collect::>(); - let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::>(); + let fneg = expected + .iter() + .filter(|x| !actual.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::>(); + let fpos = actual + .iter() + .filter(|x| !expected.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::>(); panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}"); } diff --git a/crates/ide-ssr/Cargo.toml b/crates/ide-ssr/Cargo.toml index 70ed6dea5bf28..56b29f92b8293 100644 --- a/crates/ide-ssr/Cargo.toml +++ b/crates/ide-ssr/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true triomphe.workspace = true nohash-hasher.workspace = true diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index 60fcbbbd39797..0312a0f11ebd8 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -651,7 +651,7 @@ impl Match { for (path, resolved_path) in &template.resolved_paths { if let hir::PathResolution::Def(module_def) = resolved_path.resolution { let mod_path = - module.find_use_path(sema.db, module_def, false).ok_or_else(|| { + module.find_use_path(sema.db, module_def, false, true).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") })?; self.rendered_template_paths.insert(path.clone(), mod_path); diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 2aee203c4ea54..d5c3439f95638 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -14,9 +14,9 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" -either = "1.7.0" -itertools = "0.10.5" -tracing = "0.1.35" +either.workspace = true +itertools.workspace = true +tracing.workspace = true oorandom = "11.1.3" pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.1", default-features = false } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 119a9c7c3f40a..3220774567750 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -14,12 +14,12 @@ pub struct ExpandedMacro { // Feature: Expand Macro Recursively // -// Shows the full macro expansion of the macro at current cursor. +// Shows the full macro expansion of the macro at the current caret position. // // |=== // | Editor | Action Name // -// | VS Code | **rust-analyzer: Expand macro recursively** +// | VS Code | **rust-analyzer: Expand macro recursively at caret** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index e54bc48d555bd..d3d492f3fdef0 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1136,7 +1136,9 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); @@ -1155,7 +1157,9 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); @@ -1174,7 +1178,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); @@ -1193,7 +1199,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); @@ -2005,7 +2013,10 @@ fn test_hover_layout_of_enum() { ``` ```rust - enum Foo // size = 16 (0x10), align = 8, niches = 254 + enum Foo { + Variant1(u8, u16), + Variant2(i32, u8, i64), + } // size = 16 (0x10), align = 8, niches = 254 ``` "#]], ); @@ -2346,7 +2357,7 @@ fn main() { let s$0t = S{ f1:0 }; } focus_range: 7..8, name: "S", kind: Struct, - description: "struct S", + description: "struct S {\n f1: u32,\n}", }, }, ], @@ -2379,7 +2390,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S", + description: "struct S {\n f1: T,\n}", }, }, HoverGotoTypeData { @@ -2392,7 +2403,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg", + description: "struct Arg(u32);", }, }, ], @@ -2438,7 +2449,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S", + description: "struct S {\n f1: T,\n}", }, }, HoverGotoTypeData { @@ -2451,7 +2462,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg", + description: "struct Arg(u32);", }, }, ], @@ -2487,7 +2498,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 7..8, name: "A", kind: Struct, - description: "struct A", + description: "struct A(u32);", }, }, HoverGotoTypeData { @@ -2500,7 +2511,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 22..23, name: "B", kind: Struct, - description: "struct B", + description: "struct B(u32);", }, }, HoverGotoTypeData { @@ -2514,7 +2525,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } name: "C", kind: Struct, container_name: "M", - description: "pub struct C", + description: "pub struct C(u32);", }, }, ], @@ -2704,7 +2715,7 @@ fn main() { let s$0t = foo(); } focus_range: 39..41, name: "S1", kind: Struct, - description: "struct S1", + description: "struct S1 {}", }, }, HoverGotoTypeData { @@ -2717,7 +2728,7 @@ fn main() { let s$0t = foo(); } focus_range: 52..54, name: "S2", kind: Struct, - description: "struct S2", + description: "struct S2 {}", }, }, ], @@ -2808,7 +2819,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -2908,7 +2919,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -2945,7 +2956,7 @@ fn main() { let s$0t = foo(); } focus_range: 49..50, name: "B", kind: Struct, - description: "struct B", + description: "struct B {}", }, }, HoverGotoTypeData { @@ -3034,7 +3045,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3082,7 +3093,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B", + description: "struct B {}", }, }, HoverGotoTypeData { @@ -3108,7 +3119,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3335,7 +3346,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -3356,7 +3367,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -3378,7 +3389,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -5935,7 +5946,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo // size = 4, align = 4 + pub struct Foo(i32); // size = 4, align = 4 ``` --- diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a5d070fe76738..24f44ca06ffb3 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -563,6 +563,7 @@ mod tests { use hir::ClosureStyle; use itertools::Itertools; use test_utils::extract_annotations; + use text_edit::{TextRange, TextSize}; use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::DiscriminantHints; @@ -629,6 +630,22 @@ mod tests { expect.assert_debug_eq(&inlay_hints) } + #[track_caller] + pub(super) fn check_expect_clear_loc( + config: InlayHintsConfig, + ra_fixture: &str, + expect: Expect, + ) { + let (analysis, file_id) = fixture::file(ra_fixture); + let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { + if let Some(loc) = &mut hint.linked_location { + loc.range = TextRange::empty(TextSize::from(0)); + } + }); + expect.assert_debug_eq(&inlay_hints) + } + /// Computes inlay hints for the fixture, applies all the provided text edits and then runs /// expect test. #[track_caller] diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 4152e60675515..c9e9a2237867b 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -78,7 +78,9 @@ mod tests { use expect_test::expect; use crate::{ - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::tests::{ + check_expect, check_expect_clear_loc, check_with_config, DISABLED_CONFIG, TEST_CONFIG, + }, InlayHintsConfig, }; @@ -444,7 +446,7 @@ fn main() { #[test] fn shorten_iterator_chaining_hints() { - check_expect( + check_expect_clear_loc( InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" //- minicore: iterators @@ -484,7 +486,7 @@ fn main() { file_id: FileId( 1, ), - range: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -497,7 +499,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + range: 0..0, }, ), tooltip: "", @@ -522,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -535,7 +537,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + range: 0..0, }, ), tooltip: "", @@ -560,7 +562,7 @@ fn main() { file_id: FileId( 1, ), - range: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -573,7 +575,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + range: 0..0, }, ), tooltip: "", @@ -598,7 +600,7 @@ fn main() { file_id: FileId( 0, ), - range: 24..30, + range: 0..0, }, ), tooltip: "", diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 2d0295692ac0b..f387bbf6b0143 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -683,6 +683,32 @@ enum Foo { ); } + #[test] + fn test_self() { + check( + r#" +struct S$0 { + t: PhantomData, +} + +impl S { + fn new() -> Self { + Self { + t: Default::default(), + } + } +} +"#, + expect![[r#" + S Struct FileId(0) 0..38 7..8 + + FileId(0) 48..49 + FileId(0) 71..75 + FileId(0) 86..90 + "#]], + ) + } + #[test] fn test_find_all_refs_two_modules() { check( diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml index f041ca88ac08d..31b9f6c76d067 100644 --- a/crates/load-cargo/Cargo.toml +++ b/crates/load-cargo/Cargo.toml @@ -11,13 +11,13 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true crossbeam-channel = "0.5.5" -itertools = "0.10.5" -tracing = "0.1.35" +itertools.workspace = true +tracing.workspace = true ide.workspace = true -ide-db.workspace =true +ide-db.workspace = true proc-macro-api.workspace = true project-model.workspace = true tt.workspace = true diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 7a795dd62ab70..68b592ffaa4de 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -208,6 +208,7 @@ impl ProjectFolders { let entry = { let mut dirs = vfs::loader::Directories::default(); dirs.extensions.push("rs".into()); + dirs.extensions.push("toml".into()); dirs.include.extend(root.include); dirs.exclude.extend(root.exclude); for excl in global_excludes { diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index 82105522ebdd9..adab1003d103b 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml @@ -15,7 +15,7 @@ doctest = false cov-mark = "2.0.0-pre.1" rustc-hash = "1.1.0" smallvec.workspace = true -tracing = "0.1.35" +tracing.workspace = true # local deps syntax.workspace = true diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs index 577eb0967b426..e38571dd3ec64 100644 --- a/crates/parser/src/event.rs +++ b/crates/parser/src/event.rs @@ -2,11 +2,6 @@ //! It is intended to be completely decoupled from the //! parser, so as to allow to evolve the tree representation //! and the parser algorithm independently. -//! -//! The `TreeSink` trait is the bridge between the parser and the -//! tree builder: the parser produces a stream of events like -//! `start node`, `finish node`, and `FileBuilder` converts -//! this stream to a real tree. use std::mem; use crate::{ diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 4229f289130f9..2c2d2e8a94525 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -21,7 +21,7 @@ object = { version = "0.32.0", default-features = false, features = [ ] } serde.workspace = true serde_json = { workspace = true, features = ["unbounded_depth"] } -tracing = "0.1.37" +tracing.workspace = true triomphe.workspace = true memmap2 = "0.5.4" snap = "1.1.0" diff --git a/crates/proc-macro-test/Cargo.toml b/crates/proc-macro-test/Cargo.toml index 77b4afd7d7e13..12d7c07d3ed9e 100644 --- a/crates/proc-macro-test/Cargo.toml +++ b/crates/proc-macro-test/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [build-dependencies] -cargo_metadata = "0.15.0" +cargo_metadata.workspace = true proc-macro-test-impl = { path = "imp", version = "0.0.0" } diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 937834a82aefa..56ce9d11c085f 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -14,8 +14,8 @@ doctest = false [dependencies] once_cell = "1.17.0" cfg-if = "1.0.0" -libc = "0.2.135" la-arena.workspace = true +libc.workspace = true countme = { version = "3.0.1", features = ["enable"] } jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true } diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 75977fc5b04fe..3e48de6456b0a 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -12,16 +12,16 @@ rust-version.workspace = true doctest = false [dependencies] -tracing = "0.1.35" +anyhow.workspace = true +cargo_metadata.workspace = true rustc-hash = "1.1.0" -cargo_metadata = "0.15.0" semver = "1.0.14" serde_json.workspace = true serde.workspace = true +tracing.workspace = true triomphe.workspace = true -anyhow = "1.0.62" la-arena.workspace = true -itertools = "0.10.5" +itertools.workspace = true # local deps base-db.workspace = true diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index c85b3e53cda2f..76f764460329b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -19,30 +19,26 @@ name = "rust-analyzer" path = "src/bin/main.rs" [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true crossbeam-channel = "0.5.5" -dissimilar = "1.0.4" -itertools = "0.10.5" +dissimilar.workspace = true +itertools.workspace = true scip = "0.3.1" lsp-types = { version = "=0.94.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" +rayon.workspace = true rustc-hash = "1.1.0" serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true -rayon = "1.6.1" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } lsp-server.workspace = true -tracing = "0.1.35" -tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "registry", - "fmt", - "tracing-log", -] } -tracing-log = "0.1.3" -tracing-tree = "0.2.1" +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-log = "0.2.0" +tracing-tree.workspace = true triomphe.workspace = true nohash-hasher.workspace = true always-assert = "0.1.2" @@ -81,7 +77,7 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr [dev-dependencies] expect-test = "1.4.0" -xshell = "0.2.2" +xshell.workspace = true test-utils.workspace = true sourcegen.workspace = true diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 230ff5f9b86ae..0f6539f224d79 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -762,7 +762,8 @@ impl flags::AnalysisStats { group: true, skip_glob_imports: true, }, - prefer_no_std: Default::default(), + prefer_no_std: false, + prefer_prelude: true, }, ide::AssistResolveStrategy::All, file_id, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c8df4255d96b9..f28f6ffb8748a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -352,7 +352,9 @@ config_data! { /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. imports_merge_glob: bool = "true", /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. - imports_prefer_no_std: bool = "false", + imports_preferNoStd | imports_prefer_no_std: bool = "false", + /// Whether to prefer import paths containing a `prelude` module. + imports_preferPrelude: bool = "false", /// The path structure for newly inserted paths to use. imports_prefix: ImportPrefixDef = "\"plain\"", @@ -1117,7 +1119,8 @@ impl Config { ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, }, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, } } @@ -1486,7 +1489,8 @@ impl Config { CallableCompletionDef::None => None, }, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, snippet_cap: SnippetCap::new(try_or_def!( self.caps .text_document @@ -1515,7 +1519,8 @@ impl Config { snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), allowed: None, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, assist_emit_must_use: self.data.assist_emitMustUse, } } diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 2c402552919f0..b7e839ac2a5e2 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -146,6 +146,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = @@ -186,6 +187,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 3fae08b82e27a..8dba83ed5edd2 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -380,7 +380,6 @@ impl GlobalState { ws }) .collect::>(); - // Workspaces are the same, but we've updated build data. self.workspaces = Arc::new(workspaces); } else { diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index a313507bff31b..ba36fb0b04464 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -11,10 +11,10 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ra-ap-rustc_lexer = { version = "0.18.0" } +ra-ap-rustc_lexer = { version = "0.19.0" } ra-ap-rustc_parse_format = { version = "0.14.0", default-features = false } -ra-ap-rustc_index = { version = "0.18.0", default-features = false } -ra-ap-rustc_abi = { version = "0.18.0", default-features = false } +ra-ap-rustc_index = { version = "0.19.0", default-features = false } +ra-ap-rustc_abi = { version = "0.19.0", default-features = false } [features] in-rust-tree = [] diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml index fb2b9ebef506d..0514af8e7839f 100644 --- a/crates/sourcegen/Cargo.toml +++ b/crates/sourcegen/Cargo.toml @@ -12,4 +12,4 @@ rust-version.workspace = true doctest = false [dependencies] -xshell = "0.2.2" +xshell.workspace = true diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 536f000a44b7a..35986799fdd4d 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -12,10 +12,10 @@ rust-version.workspace = true doctest = false [dependencies] -libc = "0.2.135" backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" +libc.workspace = true crossbeam-channel = "0.5.5" # Think twice before adding anything here diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index dc92366d1c7a2..3b55921dc759e 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -14,12 +14,12 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -either = "1.7.0" -itertools = "0.10.5" +either.workspace = true +itertools.workspace = true rowan = "0.15.11" rustc-hash = "1.1.0" once_cell = "1.17.0" -indexmap = "2.0.0" +indexmap.workspace = true smol_str.workspace = true triomphe.workspace = true @@ -31,7 +31,7 @@ stdx.workspace = true text-edit.workspace = true [dev-dependencies] -rayon = "1.6.1" +rayon.workspace = true expect-test = "1.4.0" proc-macro2 = "1.0.47" quote = "1.0.20" diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 3603560d35c3a..c3010d090c6b9 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -36,7 +36,7 @@ PathSegment = '::'? NameRef | NameRef GenericArgList? | NameRef ParamList RetType? -| '<' PathType ('as' PathType)? '>' +| '<' Type ('as' PathType)? '>' GenericArgList = '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>' diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index a85e1d1d9d0eb..37d8212042da0 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -3,18 +3,17 @@ use std::iter::{empty, successors}; use parser::{SyntaxKind, T}; -use rowan::SyntaxElement; use crate::{ algo::{self, neighbor}, ast::{self, edit::IndentLevel, make, HasGenericParams}, ted::{self, Position}, - AstNode, AstToken, Direction, + AstNode, AstToken, Direction, SyntaxElement, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, }; -use super::HasName; +use super::{HasArgList, HasName}; pub trait GenericParamsOwnerEdit: ast::HasGenericParams { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; @@ -362,6 +361,24 @@ impl ast::PathSegment { } } +impl ast::MethodCallExpr { + pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList { + if self.generic_arg_list().is_none() { + let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update(); + + if let Some(arg_list) = self.arg_list() { + ted::insert_raw( + ted::Position::before(arg_list.syntax()), + generic_arg_list.syntax(), + ); + } else { + ted::append_child(self.syntax(), generic_arg_list.syntax()); + } + } + self.generic_arg_list().unwrap() + } +} + impl Removable for ast::UseTree { fn remove(&self) { for dir in [Direction::Next, Direction::Prev] { @@ -559,7 +576,7 @@ impl ast::AssocItemList { None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), }, }; - let elements: Vec> = vec![ + let elements: Vec = vec![ make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ]; @@ -629,6 +646,50 @@ impl ast::MatchArmList { } } +impl ast::LetStmt { + pub fn set_ty(&self, ty: Option) { + match ty { + None => { + if let Some(colon_token) = self.colon_token() { + ted::remove(colon_token); + } + + if let Some(existing_ty) = self.ty() { + if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token() { + if sibling.kind() == SyntaxKind::WHITESPACE { + ted::remove(sibling); + } + } + + ted::remove(existing_ty.syntax()); + } + + // Remove any trailing ws + if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + Some(new_ty) => { + if self.colon_token().is_none() { + ted::insert_raw( + Position::after( + self.pat().expect("let stmt should have a pattern").syntax(), + ), + make::token(T![:]), + ); + } + + if let Some(old_ty) = self.ty() { + ted::replace(old_ty.syntax(), new_ty.syntax()); + } else { + ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax()); + } + } + } + } +} + impl ast::RecordExprFieldList { pub fn add_field(&self, field: ast::RecordExprField) { let is_multiline = self.syntax().text().contains_char('\n'); @@ -753,7 +814,7 @@ impl ast::VariantList { None => (IndentLevel::single(), Position::last_child_of(self.syntax())), }, }; - let elements: Vec> = vec![ + let elements: Vec = vec![ make::tokens::whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), ast::make::token(T![,]).into(), @@ -788,6 +849,53 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } +impl ast::IdentPat { + pub fn set_pat(&self, pat: Option) { + match pat { + None => { + if let Some(at_token) = self.at_token() { + // Remove `@ Pat` + let start = at_token.clone().into(); + let end = self + .pat() + .map(|it| it.syntax().clone().into()) + .unwrap_or_else(|| at_token.into()); + + ted::remove_all(start..=end); + + // Remove any trailing ws + if let Some(last) = + self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + } + Some(pat) => { + if let Some(old_pat) = self.pat() { + // Replace existing pattern + ted::replace(old_pat.syntax(), pat.syntax()) + } else if let Some(at_token) = self.at_token() { + // Have an `@` token but not a pattern yet + ted::insert(ted::Position::after(at_token), pat.syntax()); + } else { + // Don't have an `@`, should have a name + let name = self.name().unwrap(); + + ted::insert_all( + ted::Position::after(name.syntax()), + vec![ + make::token(T![@]).into(), + make::tokens::single_space().into(), + pat.syntax().clone().into(), + ], + ) + } + } + } + } +} + pub trait HasVisibilityEdit: ast::HasVisibility { fn set_visibility(&self, visbility: ast::Visibility) { match self.visibility() { @@ -889,6 +997,65 @@ mod tests { ); } + #[test] + fn test_ident_pat_set_pat() { + #[track_caller] + fn check(before: &str, expected: &str, pat: Option) { + let pat = pat.map(|it| it.clone_for_update()); + + let ident_pat = ast_mut_from_text::(&format!("fn f() {{ {before} }}")); + ident_pat.set_pat(pat); + + let after = ast_mut_from_text::(&format!("fn f() {{ {expected} }}")); + assert_eq!(ident_pat.to_string(), after.to_string()); + } + + // replacing + check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into())); + + // note: no trailing semicolon is added for the below tests since it + // seems to be picked up by the ident pat during error recovery? + + // adding + check("let a ", "let a @ ()", Some(make::tuple_pat([]).into())); + check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into())); + + // removing + check("let a @ ()", "let a", None); + check("let a @ ", "let a", None); + } + + #[test] + fn test_let_stmt_set_ty() { + #[track_caller] + fn check(before: &str, expected: &str, ty: Option) { + let ty = ty.map(|it| it.clone_for_update()); + + let let_stmt = ast_mut_from_text::(&format!("fn f() {{ {before} }}")); + let_stmt.set_ty(ty); + + let after = ast_mut_from_text::(&format!("fn f() {{ {expected} }}")); + assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}"); + } + + // adding + check("let a;", "let a: ();", Some(make::ty_tuple([]))); + // no semicolon due to it being eaten during error recovery + check("let a:", "let a: ()", Some(make::ty_tuple([]))); + + // replacing + check("let a: u8;", "let a: ();", Some(make::ty_tuple([]))); + check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); + check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); + + // removing + check("let a: u8;", "let a;", None); + check("let a:;", "let a;", None); + + check("let a: u8 = 3;", "let a = 3;", None); + check("let a: = 3;", "let a = 3;", None); + } + #[test] fn add_variant_to_empty_enum() { let variant = make::variant(make::name("Bar"), None).clone_for_update(); diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 7ba0d4dc656fc..6c86e591044a8 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -59,8 +59,9 @@ impl PathSegment { pub fn param_list(&self) -> Option { support::child(&self.syntax) } pub fn ret_type(&self) -> Option { support::child(&self.syntax) } pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } - pub fn path_type(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } + pub fn path_type(&self) -> Option { support::child(&self.syntax) } pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } } @@ -1576,14 +1577,6 @@ impl RecordPatField { pub fn pat(&self) -> Option { support::child(&self.syntax) } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum GenericArg { - TypeArg(TypeArg), - AssocTypeArg(AssocTypeArg), - LifetimeArg(LifetimeArg), - ConstArg(ConstArg), -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { ArrayType(ArrayType), @@ -1602,6 +1595,14 @@ pub enum Type { TupleType(TupleType), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg { + TypeArg(TypeArg), + AssocTypeArg(AssocTypeArg), + LifetimeArg(LifetimeArg), + ConstArg(ConstArg), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Expr { ArrayExpr(ArrayExpr), @@ -3319,41 +3320,6 @@ impl AstNode for RecordPatField { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl From for GenericArg { - fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } -} -impl From for GenericArg { - fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } -} -impl From for GenericArg { - fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } -} -impl From for GenericArg { - fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) } -} -impl AstNode for GenericArg { - fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG) - } - fn cast(syntax: SyntaxNode) -> Option { - let res = match syntax.kind() { - TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }), - ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }), - LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }), - CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }), - _ => return None, - }; - Some(res) - } - fn syntax(&self) -> &SyntaxNode { - match self { - GenericArg::TypeArg(it) => &it.syntax, - GenericArg::AssocTypeArg(it) => &it.syntax, - GenericArg::LifetimeArg(it) => &it.syntax, - GenericArg::ConstArg(it) => &it.syntax, - } - } -} impl From for Type { fn from(node: ArrayType) -> Type { Type::ArrayType(node) } } @@ -3455,6 +3421,41 @@ impl AstNode for Type { } } } +impl From for GenericArg { + fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } +} +impl From for GenericArg { + fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } +} +impl From for GenericArg { + fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } +} +impl From for GenericArg { + fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) } +} +impl AstNode for GenericArg { + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }), + ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }), + LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }), + CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + GenericArg::TypeArg(it) => &it.syntax, + GenericArg::AssocTypeArg(it) => &it.syntax, + GenericArg::LifetimeArg(it) => &it.syntax, + GenericArg::ConstArg(it) => &it.syntax, + } + } +} impl From for Expr { fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) } } @@ -4340,12 +4341,12 @@ impl AstNode for AnyHasVisibility { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl std::fmt::Display for GenericArg { +impl std::fmt::Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for Type { +impl std::fmt::Display for GenericArg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 31a858b91a7d4..ad63cc558629f 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -941,6 +941,13 @@ pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg { ast_from_text(&format!("const S: T<{lifetime}> = ();")) } +pub fn turbofish_generic_arg_list( + args: impl IntoIterator, +) -> ast::GenericArgList { + let args = args.into_iter().join(", "); + ast_from_text(&format!("const S: T::<{args}> = ();")) +} + pub(crate) fn generic_arg_list( args: impl IntoIterator, ) -> ast::GenericArgList { @@ -1126,7 +1133,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\n", ) }); diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 691d0c618f381..be5b954ad345f 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -361,6 +361,15 @@ impl ast::Impl { } } +// [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778) +impl ast::PathSegment { + pub fn qualifying_trait(&self) -> Option { + let mut path_types = support::children(self.syntax()); + let first = path_types.next()?; + path_types.next().or(Some(first)) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum StructKind { Record(ast::RecordFieldList), diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 2b5b6f4956166..438b599ffaae5 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] # Avoid adding deps here, this crate is widely used in tests it should compile fast! -dissimilar = "1.0.4" +dissimilar = "1.0.7" text-size.workspace = true rustc-hash = "1.1.0" diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index cc41d87f5d97c..f2ca9d82ed0dd 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -44,7 +44,7 @@ //! panic: fmt //! phantom_data: //! pin: -//! pointee: +//! pointee: copy, send, sync, ord, hash, unpin //! range: //! result: //! send: sized @@ -54,6 +54,7 @@ //! sync: sized //! transmute: //! try: infallible +//! unpin: sized //! unsize: sized #![rustc_coherence_is_core] @@ -89,6 +90,11 @@ pub mod marker { pub trait Unsize {} // endregion:unsize + // region:unpin + #[lang = "unpin"] + pub auto trait Unpin {} + // endregion:unpin + // region:copy #[lang = "copy"] pub trait Copy: Clone {} @@ -387,9 +393,10 @@ pub mod ptr { // region:pointee #[lang = "pointee_trait"] + #[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Pointee { #[lang = "metadata_type"] - type Metadata; + type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } // endregion:pointee // region:non_null diff --git a/crates/text-edit/Cargo.toml b/crates/text-edit/Cargo.toml index 76d0ca5ccb601..4620cc72d0a22 100644 --- a/crates/text-edit/Cargo.toml +++ b/crates/text-edit/Cargo.toml @@ -12,5 +12,5 @@ rust-version.workspace = true doctest = false [dependencies] -itertools = "0.10.5" +itertools.workspace = true text-size.workspace = true diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index 11055f0280ac2..fe6cb0a2c3fd9 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -12,10 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] -tracing = "0.1.35" +tracing.workspace = true walkdir = "2.3.2" crossbeam-channel = "0.5.5" -# We demand 5.1.0 as any higher version pulls in a new windows-sys dupe notify = "6.1.1" stdx.workspace = true diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index c35785cf98c4a..11409f2eb8195 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] rustc-hash = "1.1.0" fst = "0.4.7" -indexmap = "2.0.0" +indexmap.workspace = true nohash-hasher.workspace = true paths.workspace = true diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 0801e988f5ce6..b66c9c943a161 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -57,8 +57,9 @@ export interface TextDocumentEdit { } ``` -When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholder. -At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. +When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholders. +At the moment, rust-analyzer guarantees that only a single `TextDocumentEdit` will have edits which can be `InsertTextFormat.Snippet`. +Any additional `TextDocumentEdit`s will only have edits which are `InsertTextFormat.PlainText`. ### Example diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 7c76ae81bea0d..7091ea1ce9a7e 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -493,11 +493,16 @@ Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-i -- Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. -- -[[rust-analyzer.imports.prefer.no.std]]rust-analyzer.imports.prefer.no.std (default: `false`):: +[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`):: + -- Prefer to unconditionally use imports of the core and alloc crate, over the std crate. -- +[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`):: ++ +-- +Whether to prefer import paths containing a `prelude` module. +-- [[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: + -- diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index b605de4c7b82f..18d5ddd4d0afb 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -946,7 +946,7 @@ Or it is possible to specify vars more granularly: "rust-analyzer.runnables.extraEnv": [ { // "mask": null, // null mask means that this rule will be applied for all runnables - env: { + "env": { "APP_ID": "1", "APP_DATA": "asdf" } @@ -968,7 +968,7 @@ If needed, you can set different values for different platforms: "rust-analyzer.runnables.extraEnv": [ { "platform": "win32", // windows only - env: { + "env": { "APP_DATA": "windows specific data" } }, diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 20fe781ae9e40..1c94f13d74517 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -18,7 +18,7 @@ "devDependencies": { "@tsconfig/strictest": "^2.0.1", "@types/node": "~16.11.7", - "@types/vscode": "~1.75", + "@types/vscode": "~1.78.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vscode/test-electron": "^2.3.3", @@ -32,7 +32,7 @@ "typescript": "^5.1.6" }, "engines": { - "vscode": "^1.75.0" + "vscode": "^1.78.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -565,9 +565,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.75.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.1.tgz", - "integrity": "sha512-emg7wdsTFzdi+elvoyoA+Q8keEautdQHyY5LNmHVM4PTpY8JgOTVADrGVyXGepJ6dVW2OS5/xnLUWh+nZxvdiA==", + "version": "1.78.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.78.1.tgz", + "integrity": "sha512-wEA+54axejHu7DhcUfnFBan1IqFD1gBDxAFz8LoX06NbNDMRJv/T6OGthOs52yZccasKfN588EyffHWABkR0fg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { diff --git a/editors/code/package.json b/editors/code/package.json index c7b877b2897cf..265c63f3e2dc8 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -27,7 +27,7 @@ } }, "engines": { - "vscode": "^1.75.0" + "vscode": "^1.78.0" }, "enabledApiProposals": [], "scripts": { @@ -54,7 +54,7 @@ "devDependencies": { "@tsconfig/strictest": "^2.0.1", "@types/node": "~16.11.7", - "@types/vscode": "~1.75", + "@types/vscode": "~1.78.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vscode/test-electron": "^2.3.3", @@ -156,7 +156,7 @@ }, { "command": "rust-analyzer.expandMacro", - "title": "Expand macro recursively", + "title": "Expand macro recursively at caret", "category": "rust-analyzer" }, { @@ -209,11 +209,6 @@ "title": "Rebuild proc macros and build scripts", "category": "rust-analyzer" }, - { - "command": "rust-analyzer.addProject", - "title": "Add current file's crate to workspace", - "category": "rust-analyzer" - }, { "command": "rust-analyzer.restartServer", "title": "Restart server", @@ -1129,11 +1124,16 @@ "default": true, "type": "boolean" }, - "rust-analyzer.imports.prefer.no.std": { + "rust-analyzer.imports.preferNoStd": { "markdownDescription": "Prefer to unconditionally use imports of the core and alloc crate, over the std crate.", "default": false, "type": "boolean" }, + "rust-analyzer.imports.preferPrelude": { + "markdownDescription": "Whether to prefer import paths containing a `prelude` module.", + "default": false, + "type": "boolean" + }, "rust-analyzer.imports.prefix": { "markdownDescription": "The path structure for newly inserted paths to use.", "default": "plain", diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 7e24de664e9e7..3d33d255ad491 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -870,28 +870,6 @@ export function rebuildProcMacros(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.rebuildProcMacros); } -export function addProject(ctx: CtxInit): Cmd { - return async () => { - const extensionName = ctx.config.discoverProjectRunner; - // this command shouldn't be enabled in the first place if this isn't set. - if (!extensionName) { - return; - } - - const command = `${extensionName}.discoverWorkspaceCommand`; - const project: JsonProject = await vscode.commands.executeCommand(command); - - ctx.addToDiscoveredWorkspaces([project]); - - // this is a workaround to avoid needing writing the `rust-project.json` into - // a workspace-level VS Code-specific settings folder. We'd like to keep the - // `rust-project.json` entirely in-memory. - await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { - settings: "", - }); - }; -} - async function showReferencesImpl( client: LanguageClient | undefined, uri: string, diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 84d1ad98bd985..63ae386c8ad17 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import type * as lc from "vscode-languageclient/node"; +import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; import { Config, prepareVSCodeConfig } from "./config"; @@ -22,6 +22,7 @@ import { import { execRevealDependency } from "./commands"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; +import type { RustAnalyzerExtensionApi } from "./main"; // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if // only those are in use. We use "Empty" to represent these scenarios @@ -64,7 +65,7 @@ export type CtxInit = Ctx & { readonly client: lc.LanguageClient; }; -export class Ctx { +export class Ctx implements RustAnalyzerExtensionApi { readonly statusBar: vscode.StatusBarItem; config: Config; readonly workspace: Workspace; @@ -189,8 +190,11 @@ export class Ctx { if (this.config.discoverProjectRunner) { const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; log.info(`running command: ${command}`); - const project: JsonProject = await vscode.commands.executeCommand(command); - this.addToDiscoveredWorkspaces([project]); + const uris = vscode.workspace.textDocuments + .filter(isRustDocument) + .map((document) => document.uri); + const projects: JsonProject[] = await vscode.commands.executeCommand(command, uris); + this.setWorkspaces(projects); } if (this.workspace.kind === "Detached Files") { @@ -342,15 +346,17 @@ export class Ctx { return this._serverPath; } - addToDiscoveredWorkspaces(workspaces: JsonProject[]) { - for (const workspace of workspaces) { - const index = this.config.discoveredWorkspaces.indexOf(workspace); - if (~index) { - this.config.discoveredWorkspaces[index] = workspace; - } else { - this.config.discoveredWorkspaces.push(workspace); - } - } + setWorkspaces(workspaces: JsonProject[]) { + this.config.discoveredWorkspaces = workspaces; + } + + async notifyRustAnalyzer(): Promise { + // this is a workaround to avoid needing writing the `rust-project.json` into + // a workspace-level VS Code-specific settings folder. We'd like to keep the + // `rust-project.json` entirely in-memory. + await this.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", + }); } private updateCommands(forceDisable?: "disable") { diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 5de5aabc39f73..3073353674ce5 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -9,8 +9,12 @@ import { setContextValue } from "./util"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; +// This API is not stable and may break in between minor releases. export interface RustAnalyzerExtensionApi { readonly client?: lc.LanguageClient; + + setWorkspaces(workspaces: JsonProject[]): void; + notifyRustAnalyzer(): Promise; } export async function deactivate() { @@ -152,7 +156,6 @@ function createCommands(): Record { shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, rebuildProcMacros: { enabled: commands.rebuildProcMacros }, - addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index ee353c28dd670..c74284a00d964 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -2,14 +2,14 @@ "extends": "@tsconfig/strictest/tsconfig.json", "compilerOptions": { "esModuleInterop": false, - "module": "commonjs", - "moduleResolution": "node16", - "target": "es2021", + "module": "CommonJS", + "moduleResolution": "Node16", + "target": "ES2021", "outDir": "out", - "lib": ["es2021"], + "lib": ["ES2021"], "sourceMap": true, "rootDir": ".", - "newLine": "LF", + "newLine": "lf", // FIXME: https://github.com/rust-lang/rust-analyzer/issues/15253 "exactOptionalPropertyTypes": false diff --git a/lib/line-index/Cargo.toml b/lib/line-index/Cargo.toml index 6c0d06f471780..b7b4a01818ea7 100644 --- a/lib/line-index/Cargo.toml +++ b/lib/line-index/Cargo.toml @@ -7,5 +7,5 @@ repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-in edition = "2021" [dependencies] -text-size = "1.1.0" +text-size = "1.1.1" nohash-hasher = "0.2.0" diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 7ec3247e943aa..8d00813b0d7a4 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] log = "0.4.17" -serde_json = "1.0.96" -serde = { version = "1.0.156", features = ["derive"] } +serde_json = "1.0.108" +serde = { version = "1.0.192", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 7a34617e2558c..1c785b60a3d8f 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" rust-version.workspace = true [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true flate2 = "1.0.24" write-json = "0.1.2" -xshell = "0.2.2" +xshell.workspace = true xflags = "0.3.0" time = { version = "0.3", default-features = false } zip = { version = "0.6", default-features = false, features = ["deflate", "time"] } From 808f6687f7b796dd2c171e3be3829f1dedb6d537 Mon Sep 17 00:00:00 2001 From: austaras Date: Fri, 17 Nov 2023 16:47:46 +0800 Subject: [PATCH 10/31] address comment --- crates/hir-ty/src/infer/unify.rs | 31 +--------------- crates/hir-ty/src/method_resolution.rs | 51 +++++++++++++++++--------- crates/hir-ty/src/tests/traits.rs | 28 ++++++++++++++ 3 files changed, 62 insertions(+), 48 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 78bdd62a2769f..ac39bdf5bf53c 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -43,7 +43,7 @@ where } impl> Canonicalized { - pub(super) fn apply_solution( + pub(crate) fn apply_solution( &self, ctx: &mut InferenceTable<'_>, solution: Canonical, @@ -495,35 +495,6 @@ impl<'a> InferenceTable<'a> { solution } - pub(crate) fn try_resolve_alias(&mut self, goal: Goal) -> bool { - let in_env = InEnvironment::new(&self.trait_env.env, goal); - let canonicalized = self.canonicalize(in_env); - let solution = self.db.trait_solve( - self.trait_env.krate, - self.trait_env.block, - canonicalized.value.clone(), - ); - - match solution { - Some(Solution::Unique(canonical_subst)) => { - canonicalized.apply_solution( - self, - Canonical { - binders: canonical_subst.binders, - value: canonical_subst.value.subst, - }, - ); - true - } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs); - true - } - Some(_) => true, - None => false, - } - } - pub(crate) fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 04a55c3536dff..732643566a2d6 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -27,8 +27,9 @@ use crate::{ primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment, - Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, + InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, }; /// This is used as a key for indexing impls. @@ -1478,11 +1479,8 @@ fn is_valid_fn_candidate( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. let predicates = db.generic_predicates(impl_id.into()); - let mut alias = Vec::new(); - let mut other_predicate = Vec::new(); - - for predicate in predicates.iter() { - let (p, b) = predicate + let goals = predicates.iter().map(|p| { + let (p, b) = p .clone() .substitute(Interner, &impl_subst) // Skipping the inner binders is ok, as we don't handle quantified where @@ -1490,21 +1488,38 @@ fn is_valid_fn_candidate( .into_value_and_skipped_binders(); stdx::always!(b.len(Interner) == 0); - if let WhereClause::AliasEq(_) = p { - alias.push(p); - } else { - other_predicate.push(p); - } - } + p.cast::(Interner) + }); - for p in alias { - if !table.try_resolve_alias(p.cast(Interner)) { - return IsValidCandidate::No; + for goal in goals.clone() { + let in_env = InEnvironment::new(&table.trait_env.env, goal); + let canonicalized = table.canonicalize(in_env); + let solution = table.db.trait_solve( + table.trait_env.krate, + table.trait_env.block, + canonicalized.value.clone(), + ); + + match solution { + Some(Solution::Unique(canonical_subst)) => { + canonicalized.apply_solution( + table, + Canonical { + binders: canonical_subst.binders, + value: canonical_subst.value.subst, + }, + ); + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(table, substs); + } + Some(_) => (), + None => return IsValidCandidate::No, } } - for p in other_predicate { - if table.try_obligation(p.cast(Interner)).is_none() { + for goal in goals { + if table.try_obligation(goal).is_none() { return IsValidCandidate::No; } } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 48dd9540329d6..003ae60e8e517 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -2597,6 +2597,34 @@ fn test() { ); } +#[test] +fn associated_type_in_type_bound() { + check_types( + r#" +//- minicore: deref +fn fb(f: Foo<&u8>) { + f.foobar(); + //^^^^^^^^^^ u8 +} +trait Bar { + fn bar(&self) -> u8; +} +impl Bar for u8 { + fn bar(&self) -> u8 { *self } +} + +struct Foo { + foo: F, +} +impl> Foo { + fn foobar(&self) -> u8 { + self.foo.deref().bar() + } +} +"#, + ) +} + #[test] fn dyn_trait_through_chalk() { check_types( From 1cbda612bc2d95c14bb0b511c9d8024316dffc8b Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Fri, 17 Nov 2023 21:31:33 +0800 Subject: [PATCH 11/31] chore: add use case for `PathSegment::qualifying_trait` --- crates/ide-assists/src/handlers/unqualify_method_call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-assists/src/handlers/unqualify_method_call.rs b/crates/ide-assists/src/handlers/unqualify_method_call.rs index 0bf1782a489de..0876246e90b10 100644 --- a/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -91,7 +91,7 @@ fn add_import( ) { if let Some(path_segment) = qualifier.segment() { // for `` - let path_type = path_segment.syntax().children().filter_map(ast::PathType::cast).last(); + let path_type = path_segment.qualifying_trait(); let import = match path_type { Some(it) => { if let Some(path) = it.path() { From a54c0dafc8b670802b8ba8dc8ba1a65766cdd08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 17 Nov 2023 20:58:43 +0200 Subject: [PATCH 12/31] Remove debugging code in path resolution --- crates/hir-def/src/nameres/path_resolution.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 460a908b6db2c..4c1b8f306c50f 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -183,15 +183,6 @@ impl DefMap { shadow: BuiltinShadowMode, expected_macro_subns: Option, ) -> ResolvePathResult { - let graph = db.crate_graph(); - let _cx = stdx::panic_context::enter(format!( - "DefMap {:?} crate_name={:?} block={:?} path={}", - self.krate, - graph[self.krate].display_name, - self.block, - path.display(db.upcast()) - )); - let mut segments = path.segments().iter().enumerate(); let mut curr_per_ns = match path.kind { PathKind::DollarCrate(krate) => { From 9f8191b62d0a5c830e605e090d2dbc476101f05b Mon Sep 17 00:00:00 2001 From: Dash Lu Date: Tue, 21 Nov 2023 22:41:49 +0800 Subject: [PATCH 13/31] Update Arch Linux package URL in manual.adoc --- docs/user/manual.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 18d5ddd4d0afb..9fc19a7d07450 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -174,7 +174,7 @@ $ rustup component add rust-analyzer The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): -- https://www.archlinux.org/packages/community/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source) +- https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source) - https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version) Install it with pacman, for example: From 81c2d3552e6d377bf0332523c1278b7e5723c27c Mon Sep 17 00:00:00 2001 From: Marc Schreiber Date: Tue, 14 Nov 2023 21:37:40 +0100 Subject: [PATCH 14/31] Cancelable Initialization This commit provides additional initialization methods to Connection in order to support CTRL + C sigterm handling. --- Cargo.lock | 32 ++++++-- lib/lsp-server/Cargo.toml | 1 + lib/lsp-server/src/lib.rs | 169 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 188 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 701e36d74a657..11283034ea2fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5080df6b0f0ecb76cab30808f00d937ba725cebe266a3da8cd89dff92f2a9916" dependencies = [ - "nix", + "nix 0.26.2", "winapi", ] @@ -289,6 +289,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ctrlc" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" +dependencies = [ + "nix 0.27.1", + "windows-sys 0.48.0", +] + [[package]] name = "dashmap" version = "5.4.0" @@ -961,6 +971,7 @@ name = "lsp-server" version = "0.7.4" dependencies = [ "crossbeam-channel", + "ctrlc", "log", "lsp-types", "serde", @@ -1100,6 +1111,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -1701,18 +1723,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 8d00813b0d7a4..be1573913ff24 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -14,3 +14,4 @@ crossbeam-channel = "0.5.6" [dev-dependencies] lsp-types = "=0.94" +ctrlc = "3.4.1" diff --git a/lib/lsp-server/src/lib.rs b/lib/lsp-server/src/lib.rs index affab60a22783..b190c0af73d73 100644 --- a/lib/lsp-server/src/lib.rs +++ b/lib/lsp-server/src/lib.rs @@ -17,7 +17,7 @@ use std::{ net::{TcpListener, TcpStream, ToSocketAddrs}, }; -use crossbeam_channel::{Receiver, Sender}; +use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; pub use crate::{ error::{ExtractError, ProtocolError}, @@ -113,11 +113,62 @@ impl Connection { /// } /// ``` pub fn initialize_start(&self) -> Result<(RequestId, serde_json::Value), ProtocolError> { - loop { - break match self.receiver.recv() { - Ok(Message::Request(req)) if req.is_initialize() => Ok((req.id, req.params)), + self.initialize_start_while(|| true) + } + + /// Starts the initialization process by waiting for an initialize as described in + /// [`Self::initialize_start`] as long as `running` returns + /// `true` while the return value can be changed through a sig handler such as `CTRL + C`. + /// + /// # Example + /// + /// ```rust + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// use std::sync::Arc; + /// # use std::error::Error; + /// # use lsp_types::{ClientCapabilities, InitializeParams, ServerCapabilities}; + /// # use lsp_server::{Connection, Message, Request, RequestId, Response}; + /// # fn main() -> Result<(), Box> { + /// let running = Arc::new(AtomicBool::new(true)); + /// # running.store(true, Ordering::SeqCst); + /// let r = running.clone(); + /// + /// ctrlc::set_handler(move || { + /// r.store(false, Ordering::SeqCst); + /// }).expect("Error setting Ctrl-C handler"); + /// + /// let (connection, io_threads) = Connection::stdio(); + /// + /// let res = connection.initialize_start_while(|| running.load(Ordering::SeqCst)); + /// # assert!(res.is_err()); + /// + /// # Ok(()) + /// # } + /// ``` + pub fn initialize_start_while( + &self, + running: C, + ) -> Result<(RequestId, serde_json::Value), ProtocolError> + where + C: Fn() -> bool, + { + while running() { + let msg = match self.receiver.recv_timeout(std::time::Duration::from_secs(1)) { + Ok(msg) => msg, + Err(RecvTimeoutError::Timeout) => { + continue; + } + Err(e) => { + return Err(ProtocolError(format!( + "expected initialize request, got error: {e}" + ))) + } + }; + + match msg { + Message::Request(req) if req.is_initialize() => return Ok((req.id, req.params)), // Respond to non-initialize requests with ServerNotInitialized - Ok(Message::Request(req)) => { + Message::Request(req) => { let resp = Response::new_err( req.id.clone(), ErrorCode::ServerNotInitialized as i32, @@ -126,15 +177,18 @@ impl Connection { self.sender.send(resp.into()).unwrap(); continue; } - Ok(Message::Notification(n)) if !n.is_exit() => { + Message::Notification(n) if !n.is_exit() => { continue; } - Ok(msg) => Err(ProtocolError(format!("expected initialize request, got {msg:?}"))), - Err(e) => { - Err(ProtocolError(format!("expected initialize request, got error: {e}"))) + msg => { + return Err(ProtocolError(format!("expected initialize request, got {msg:?}"))); } }; } + + return Err(ProtocolError(String::from( + "Initialization has been aborted during initialization", + ))); } /// Finishes the initialization process by sending an `InitializeResult` to the client @@ -156,6 +210,51 @@ impl Connection { } } + /// Finishes the initialization process as described in [`Self::initialize_finish`] as + /// long as `running` returns `true` while the return value can be changed through a sig + /// handler such as `CTRL + C`. + pub fn initialize_finish_while( + &self, + initialize_id: RequestId, + initialize_result: serde_json::Value, + running: C, + ) -> Result<(), ProtocolError> + where + C: Fn() -> bool, + { + let resp = Response::new_ok(initialize_id, initialize_result); + self.sender.send(resp.into()).unwrap(); + + while running() { + let msg = match self.receiver.recv_timeout(std::time::Duration::from_secs(1)) { + Ok(msg) => msg, + Err(RecvTimeoutError::Timeout) => { + continue; + } + Err(e) => { + return Err(ProtocolError(format!( + "expected initialized notification, got error: {e}", + ))); + } + }; + + match msg { + Message::Notification(n) if n.is_initialized() => { + return Ok(()); + } + msg => { + return Err(ProtocolError(format!( + r#"expected initialized notification, got: {msg:?}"# + ))); + } + } + } + + return Err(ProtocolError(String::from( + "Initialization has been aborted during initialization", + ))); + } + /// Initialize the connection. Sends the server capabilities /// to the client and returns the serialized client capabilities /// on success. If more fine-grained initialization is required use @@ -198,6 +297,58 @@ impl Connection { Ok(params) } + /// Initialize the connection as described in [`Self::initialize`] as long as `running` returns + /// `true` while the return value can be changed through a sig handler such as `CTRL + C`. + /// + /// # Example + /// + /// ```rust + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// use std::sync::Arc; + /// # use std::error::Error; + /// # use lsp_types::ServerCapabilities; + /// # use lsp_server::{Connection, Message, Request, RequestId, Response}; + /// + /// # fn main() -> Result<(), Box> { + /// let running = Arc::new(AtomicBool::new(true)); + /// # running.store(true, Ordering::SeqCst); + /// let r = running.clone(); + /// + /// ctrlc::set_handler(move || { + /// r.store(false, Ordering::SeqCst); + /// }).expect("Error setting Ctrl-C handler"); + /// + /// let (connection, io_threads) = Connection::stdio(); + /// + /// let server_capabilities = serde_json::to_value(&ServerCapabilities::default()).unwrap(); + /// let initialization_params = connection.initialize_while( + /// server_capabilities, + /// || running.load(Ordering::SeqCst) + /// ); + /// + /// # assert!(initialization_params.is_err()); + /// # Ok(()) + /// # } + /// ``` + pub fn initialize_while( + &self, + server_capabilities: serde_json::Value, + running: C, + ) -> Result + where + C: Fn() -> bool, + { + let (id, params) = self.initialize_start_while(&running)?; + + let initialize_data = serde_json::json!({ + "capabilities": server_capabilities, + }); + + self.initialize_finish_while(id, initialize_data, running)?; + + Ok(params) + } + /// If `req` is `Shutdown`, respond to it and return `true`, otherwise return `false` pub fn handle_shutdown(&self, req: &Request) -> Result { if !req.is_shutdown() { From 886eaa0a7d2b1d7098fd49ec56e3b6f65a457449 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sun, 24 Sep 2023 18:27:15 +0200 Subject: [PATCH 15/31] Relaxation for crate graph mergin Partially fixes #15656 . When a crate graph is extended which is the case when new workspaces are added to the project the rules for deduplication were too strict. One problem that arises from this is that in certain conditions when we see the same crate having different `CrateOrigin`s the first form would be maintained. This approach however results in some unwanted results such as making renaming forbidden as this has been recently only made available for local crates. The given example in #15656 can still not be resolved with this PR as that involves taking inconsistencies between dependencies into consideration. This will be addressed in a future PR. --- crates/base-db/src/fixture.rs | 28 ++- crates/base-db/src/input.rs | 221 +++++++++++++++--- crates/base-db/src/lib.rs | 1 + crates/cfg/src/lib.rs | 4 +- crates/project-model/src/project_json.rs | 3 +- crates/project-model/src/workspace.rs | 67 ++++-- .../cargo_hello_world_project_model.txt | 7 + ...project_model_with_selective_overrides.txt | 7 + ..._project_model_with_wildcard_overrides.txt | 7 + ...rust_project_hello_world_project_model.txt | 16 ++ crates/rust-analyzer/tests/slow-tests/main.rs | 208 ++++++++++++++++- 11 files changed, 515 insertions(+), 54 deletions(-) diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 3f5ccb621c767..3da555a47acb2 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -13,9 +13,9 @@ use vfs::{file_set::FileSet, VfsPath}; use crate::{ input::{CrateName, CrateOrigin, LangCrateOrigin}, - Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, - FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel, - SourceDatabaseExt, SourceRoot, SourceRootId, + Change, CrateDisplayName, CrateGraph, CrateId, Dependency, DependencyKind, Edition, Env, + FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, + ProcMacros, ReleaseChannel, SourceDatabaseExt, SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -237,7 +237,12 @@ impl ChangeFixture { crate_graph .add_dep( from_id, - Dependency::with_prelude(CrateName::new(&to).unwrap(), to_id, prelude), + Dependency::with_prelude( + CrateName::new(&to).unwrap(), + to_id, + prelude, + DependencyKind::Normal, + ), ) .unwrap(); } @@ -275,7 +280,14 @@ impl ChangeFixture { for krate in all_crates { crate_graph - .add_dep(krate, Dependency::new(CrateName::new("core").unwrap(), core_crate)) + .add_dep( + krate, + Dependency::new( + CrateName::new("core").unwrap(), + core_crate, + DependencyKind::Normal, + ), + ) .unwrap(); } } @@ -317,7 +329,11 @@ impl ChangeFixture { crate_graph .add_dep( krate, - Dependency::new(CrateName::new("proc_macros").unwrap(), proc_macros_crate), + Dependency::new( + CrateName::new("proc_macros").unwrap(), + proc_macros_crate, + DependencyKind::Normal, + ), ) .unwrap(); } diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 65db5c0fc7d31..fb69e667ad95b 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -8,7 +8,7 @@ use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync}; -use cfg::CfgOptions; +use cfg::{CfgDiff, CfgOptions}; use la_arena::{Arena, Idx}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::SmolStr; @@ -155,6 +155,10 @@ impl CrateOrigin { pub fn is_local(&self) -> bool { matches!(self, CrateOrigin::Local { .. }) } + + pub fn is_lib(&self) -> bool { + matches!(self, CrateOrigin::Library { .. }) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -324,6 +328,99 @@ pub struct CrateData { pub channel: Option, } +impl CrateData { + /** + Check if [`other`] is almost equal to [`self`]. + This method has some obscure bits. These are mostly there to be compliant with + some patches. References to the patches are given. + */ + pub fn almost_eq(&self, other: &CrateData) -> bool { + if self.root_file_id != other.root_file_id { + return false; + } + + if self.display_name != other.display_name { + return false; + } + + if self.is_proc_macro != other.is_proc_macro { + return false; + } + + if self.edition != other.edition { + return false; + } + + if self.version != other.version { + return false; + } + + let mut opts = self.cfg_options.clone(); + opts.apply_diff(CfgDiff { + disable: other.cfg_options.clone().into_iter().collect(), + enable: vec![], + }); + + let mut cfgs = opts.into_iter(); + if let Some(cfg) = cfgs.next() { + // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. + // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 + if !cfgs.next().is_none() || cfg.to_string() != "rust_analyzer" { + return false; + } + } + + let mut itself = self.dependencies.iter(); + let mut otself = other.dependencies.iter(); + let (mut anx, mut bnx) = (itself.next(), otself.next()); + loop { + match (anx, bnx) { + (None, None) => { + break; + } + (None, Some(b)) => { + if b.kind != DependencyKind::Normal { + bnx = otself.next(); + } else { + break; + } + } + (Some(a), None) => { + if a.kind != DependencyKind::Normal { + anx = itself.next(); + } else { + break; + } + } + (Some(a), Some(b)) => { + if a.kind != DependencyKind::Normal { + anx = itself.next(); + continue; + } + + if b.kind != DependencyKind::Normal { + bnx = otself.next(); + continue; + } + + if a != b { + return false; + } + + anx = itself.next(); + bnx = otself.next(); + } + } + } + + if self.env != other.env { + return false; + } + + true + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Edition { Edition2015, @@ -351,26 +448,43 @@ impl Env { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum DependencyKind { + Normal, + Dev, + Build, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, pub name: CrateName, + kind: DependencyKind, prelude: bool, } impl Dependency { - pub fn new(name: CrateName, crate_id: CrateId) -> Self { - Self { name, crate_id, prelude: true } + pub fn new(name: CrateName, crate_id: CrateId, kind: DependencyKind) -> Self { + Self { name, crate_id, prelude: true, kind } } - pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self { - Self { name, crate_id, prelude } + pub fn with_prelude( + name: CrateName, + crate_id: CrateId, + prelude: bool, + kind: DependencyKind, + ) -> Self { + Self { name, crate_id, prelude, kind } } /// Whether this dependency is to be added to the depending crate's extern prelude. pub fn is_prelude(&self) -> bool { self.prelude } + + pub fn kind(&self) -> &DependencyKind { + &self.kind + } } impl CrateGraph { @@ -572,25 +686,41 @@ impl CrateGraph { /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted. pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) { + enum ExtendStrategy { + Dedup(CrateId), + Replace(CrateId), + } + let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); - for topo in topo { let crate_data = &mut other.arena[topo]; - crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); - crate_data.dependencies.sort_by_key(|dep| dep.crate_id); - let res = self.arena.iter().find_map( - |(id, data)| { - if data == crate_data { - Some(id) - } else { - None + crate_data.dependencies.iter_mut().for_each(|dep| { + dep.crate_id = id_map[&dep.crate_id]; + }); + crate_data.dependencies.sort_by_key(|dep| dep.crate_id); + let res = self.arena.iter().find_map(|(id, data)| { + if data.almost_eq(crate_data) { + if data.origin.is_lib() && crate_data.origin.is_local() { + // See #15656 for a relevant example. + return Some(ExtendStrategy::Replace(id)); } - }, - ); + + return Some(ExtendStrategy::Dedup(id)); + } + None + }); + if let Some(res) = res { - id_map.insert(topo, res); + match res { + ExtendStrategy::Dedup(res) => id_map.insert(topo, res), + ExtendStrategy::Replace(res) => { + let id = self.arena.alloc(crate_data.clone()); + let _ = self.remove_and_replace(res, id); + id_map.insert(topo, id) + } + }; } else { let id = self.arena.alloc(crate_data.clone()); id_map.insert(topo, id); @@ -636,9 +766,11 @@ impl CrateGraph { match (cfg_if, std) { (Some(cfg_if), Some(std)) => { self.arena[cfg_if].dependencies.clear(); - self.arena[std] - .dependencies - .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if)); + self.arena[std].dependencies.push(Dependency::new( + CrateName::new("cfg_if").unwrap(), + cfg_if, + DependencyKind::Normal, + )); true } _ => false, @@ -759,7 +891,7 @@ impl fmt::Display for CyclicDependenciesError { #[cfg(test)] mod tests { - use crate::CrateOrigin; + use crate::{CrateOrigin, DependencyKind}; use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; @@ -806,13 +938,22 @@ mod tests { None, ); assert!(graph - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) + .add_dep( + crate1, + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) + ) .is_ok()); assert!(graph - .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) + .add_dep( + crate2, + Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal) + ) .is_ok()); assert!(graph - .add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1)) + .add_dep( + crate3, + Dependency::new(CrateName::new("crate1").unwrap(), crate1, DependencyKind::Normal) + ) .is_err()); } @@ -846,10 +987,16 @@ mod tests { None, ); assert!(graph - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) + .add_dep( + crate1, + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) + ) .is_ok()); assert!(graph - .add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) + .add_dep( + crate2, + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) + ) .is_err()); } @@ -896,10 +1043,16 @@ mod tests { None, ); assert!(graph - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) + .add_dep( + crate1, + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) + ) .is_ok()); assert!(graph - .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) + .add_dep( + crate2, + Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal) + ) .is_ok()); } @@ -935,12 +1088,20 @@ mod tests { assert!(graph .add_dep( crate1, - Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2) + Dependency::new( + CrateName::normalize_dashes("crate-name-with-dashes"), + crate2, + DependencyKind::Normal + ) ) .is_ok()); assert_eq!( graph[crate1].dependencies, - vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)] + vec![Dependency::new( + CrateName::new("crate_name_with_dashes").unwrap(), + crate2, + DependencyKind::Normal + )] ); } } diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index c5c4afa30f792..40cfab88afdbe 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -12,6 +12,7 @@ use rustc_hash::FxHashSet; use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; use triomphe::Arc; +pub use crate::input::DependencyKind; pub use crate::{ change::Change, input::{ diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 0aeb0b0505248..90dba008ad70e 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -115,8 +115,8 @@ impl<'a> IntoIterator for &'a CfgOptions { #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. - enable: Vec, - disable: Vec, + pub enable: Vec, + pub disable: Vec, } impl CfgDiff { diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 80897f7478cf9..931eba1157663 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -49,7 +49,7 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition}; +use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, DependencyKind, Edition}; use la_arena::RawIdx; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; @@ -135,6 +135,7 @@ impl ProjectJson { Dependency::new( dep_data.name, CrateId::from_raw(RawIdx::from(dep_data.krate as u32)), + DependencyKind::Normal, ) }) .collect::>(), diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index e0209ca15a539..9333570354a0c 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -6,8 +6,8 @@ use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, use anyhow::{format_err, Context}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind, + Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; @@ -834,7 +834,7 @@ fn project_json_to_crate_graph( for dep in &krate.deps { if let Some(&to) = crates.get(&dep.crate_id) { - add_dep(crate_graph, from, dep.name.clone(), to) + add_dep(crate_graph, from, dep.name.clone(), to, dep.kind().to_owned()) } } } @@ -979,7 +979,7 @@ fn cargo_to_crate_graph( // cargo metadata does not do any normalization, // so we do it ourselves currently let name = CrateName::normalize_dashes(&name); - add_dep(crate_graph, from, name, to); + add_dep(crate_graph, from, name, to, DependencyKind::Normal); } } } @@ -999,7 +999,17 @@ fn cargo_to_crate_graph( continue; } - add_dep(crate_graph, from, name.clone(), to) + add_dep( + crate_graph, + from, + name.clone(), + to, + match dep.kind { + DepKind::Normal => DependencyKind::Normal, + DepKind::Dev => DependencyKind::Dev, + DepKind::Build => DependencyKind::Build, + }, + ) } } } @@ -1187,7 +1197,17 @@ fn handle_rustc_crates( let name = CrateName::new(&dep.name).unwrap(); if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { - add_dep(crate_graph, from, name.clone(), to); + add_dep( + crate_graph, + from, + name.clone(), + to, + match dep.kind { + DepKind::Normal => DependencyKind::Normal, + DepKind::Dev => DependencyKind::Dev, + DepKind::Build => DependencyKind::Build, + }, + ); } } } @@ -1209,7 +1229,7 @@ fn handle_rustc_crates( // `rust_analyzer` thinks that it should use the one from the `rustc_source` // instead of the one from `crates.io` if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) { - add_dep(crate_graph, *from, name.clone(), to); + add_dep(crate_graph, *from, name.clone(), to, DependencyKind::Normal); } } } @@ -1308,7 +1328,14 @@ impl SysrootPublicDeps { /// Makes `from` depend on the public sysroot crates. fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) { for (name, krate, prelude) in &self.deps { - add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude); + add_dep_with_prelude( + crate_graph, + from, + name.clone(), + *krate, + *prelude, + DependencyKind::Normal, + ); } } } @@ -1363,7 +1390,7 @@ fn sysroot_to_crate_graph( for &to in sysroot[from].deps.iter() { let name = CrateName::new(&sysroot[to].name).unwrap(); if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { - add_dep(crate_graph, from, name, to); + add_dep(crate_graph, from, name, to, DependencyKind::Normal); } } } @@ -1442,8 +1469,14 @@ fn handle_hack_cargo_workspace( .collect() } -fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { - add_dep_inner(graph, from, Dependency::new(name, to)) +fn add_dep( + graph: &mut CrateGraph, + from: CrateId, + name: CrateName, + to: CrateId, + kind: DependencyKind, +) { + add_dep_inner(graph, from, Dependency::new(name, to, kind)) } fn add_dep_with_prelude( @@ -1452,12 +1485,20 @@ fn add_dep_with_prelude( name: CrateName, to: CrateId, prelude: bool, + kind: DependencyKind, ) { - add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude)) + add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude, kind)) } fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) { - add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude); + add_dep_with_prelude( + crate_graph, + from, + CrateName::new("proc_macro").unwrap(), + to, + prelude, + DependencyKind::Normal, + ); } fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 727d39a3077cd..e98f016ca7df3 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -48,6 +48,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -112,6 +113,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -119,6 +121,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -183,6 +186,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -190,6 +194,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -254,6 +259,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -261,6 +267,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 727d39a3077cd..e98f016ca7df3 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -48,6 +48,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -112,6 +113,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -119,6 +121,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -183,6 +186,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -190,6 +194,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -254,6 +259,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -261,6 +267,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 89728babd82ef..7ecd53572e2f3 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -47,6 +47,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -110,6 +111,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -117,6 +119,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -180,6 +183,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -187,6 +191,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], @@ -250,6 +255,7 @@ name: CrateName( "hello_world", ), + kind: Normal, prelude: true, }, Dependency { @@ -257,6 +263,7 @@ name: CrateName( "libc", ), + kind: Normal, prelude: true, }, ], diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index b7bf6cb2774ea..581a6afc148fa 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -28,6 +28,7 @@ name: CrateName( "core", ), + kind: Normal, prelude: true, }, ], @@ -168,6 +169,7 @@ name: CrateName( "std", ), + kind: Normal, prelude: true, }, Dependency { @@ -175,6 +177,7 @@ name: CrateName( "core", ), + kind: Normal, prelude: true, }, ], @@ -249,6 +252,7 @@ name: CrateName( "alloc", ), + kind: Normal, prelude: true, }, Dependency { @@ -256,6 +260,7 @@ name: CrateName( "panic_unwind", ), + kind: Normal, prelude: true, }, Dependency { @@ -263,6 +268,7 @@ name: CrateName( "panic_abort", ), + kind: Normal, prelude: true, }, Dependency { @@ -270,6 +276,7 @@ name: CrateName( "core", ), + kind: Normal, prelude: true, }, Dependency { @@ -277,6 +284,7 @@ name: CrateName( "profiler_builtins", ), + kind: Normal, prelude: true, }, Dependency { @@ -284,6 +292,7 @@ name: CrateName( "unwind", ), + kind: Normal, prelude: true, }, Dependency { @@ -291,6 +300,7 @@ name: CrateName( "std_detect", ), + kind: Normal, prelude: true, }, Dependency { @@ -298,6 +308,7 @@ name: CrateName( "test", ), + kind: Normal, prelude: true, }, ], @@ -438,6 +449,7 @@ name: CrateName( "core", ), + kind: Normal, prelude: true, }, Dependency { @@ -445,6 +457,7 @@ name: CrateName( "alloc", ), + kind: Normal, prelude: true, }, Dependency { @@ -452,6 +465,7 @@ name: CrateName( "std", ), + kind: Normal, prelude: true, }, Dependency { @@ -459,6 +473,7 @@ name: CrateName( "test", ), + kind: Normal, prelude: false, }, Dependency { @@ -466,6 +481,7 @@ name: CrateName( "proc_macro", ), + kind: Normal, prelude: false, }, ], diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index d59914298991c..423c07183e25f 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -21,12 +21,12 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; use lsp_types::{ notification::DidOpenTextDocument, request::{ - CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, + CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, Rename, WillRenameFiles, WorkspaceSymbolRequest, }, CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams, - PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, + PartialResultParams, Position, Range, RenameFilesParams, RenameParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; @@ -1131,3 +1131,207 @@ version = "0.0.0" server.request::(Default::default(), json!([])); } + +#[test] +fn test_deduplicate_crate_differing_in_origin() { + let fixture = r#" +//- /projects/p1/Cargo.toml +[package] +name = "p1" +version = "0.0.0" + +//- /projects/p1/src/lib.rs +pub fn add2(left: usize, right: usize) -> usize { + left + right +} + +//- /projects/p2/Cargo.toml +[package] +name = "p2" +version = "0.0.0" + +[dependencies] +p1 = { path = "../p1" } + +//- /projects/p2/src/lib.rs +use p1::add2; + +pub fn bar() {} + "#; + + let server = Project::with_fixture(fixture) + .with_config(serde_json::json!({ + "linkedProjects" : [ + "./projects/p1/Cargo.toml", + "./projects/p2/Cargo.toml" + ], + } + )) + .with_config(serde_json::json!({ + "cargo": { "sysroot": null }, + })) + .server() + .wait_until_workspace_is_loaded(); + + let doc_id = server.doc_id("./projects/p2/src/lib.rs"); + let doc2_id = server.doc_id("./projects/p1/src/lib.rs"); + + server.request::( + RenameParams { + text_document_position: TextDocumentPositionParams { + text_document: doc_id.clone(), + position: Position { line: 0, character: 8 }, + }, + new_name: "ABC".to_owned(), + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + }, + json!({ + "documentChanges": [ + { + "textDocument": { + "uri": doc2_id.uri, + "version": null + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "newText": "ABC" + } + ] + }, + { + "textDocument": { + "uri": doc_id.uri, + "version": null + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 8 + }, + "end": { + "line": 0, + "character": 12 + } + }, + "newText": "ABC" + } + ] + }, + ] + }), + ); +} + +#[test] +fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { + let fixture = r#" +//- /projects/p1/Cargo.toml +[package] +name = "p1" +version = "0.0.0" + +//- /projects/p1/src/lib.rs +pub fn add2(left: usize, right: usize) -> usize { + left + right +} + +//- /projects/p2/Cargo.toml +[package] +name = "p2" +version = "0.0.0" + +[dependencies] +p1 = { path = "../p1" } + +//- /projects/p2/src/lib.rs +use p1::add2; + +pub fn bar() {} + "#; + + let server = Project::with_fixture(fixture) + .with_config(serde_json::json!({ + "linkedProjects" : [ + "./projects/p2/Cargo.toml", + "./projects/p1/Cargo.toml", + ], + } + )) + .with_config(serde_json::json!({ + "cargo": { "sysroot": null }, + })) + .server() + .wait_until_workspace_is_loaded(); + + let doc_id = server.doc_id("./projects/p2/src/lib.rs"); + let doc2_id = server.doc_id("./projects/p1/src/lib.rs"); + + server.request::( + RenameParams { + text_document_position: TextDocumentPositionParams { + text_document: doc_id.clone(), + position: Position { line: 0, character: 8 }, + }, + new_name: "ABC".to_owned(), + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + }, + json!({ + "documentChanges": [ + { + "textDocument": { + "uri": doc2_id.uri, + "version": null + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "newText": "ABC" + } + ] + }, + { + "textDocument": { + "uri": doc_id.uri, + "version": null + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 8 + }, + "end": { + "line": 0, + "character": 12 + } + }, + "newText": "ABC" + } + ] + }, + ] + }), + ); +} From 7e4aad5ba570745ef8924f1fa6ad90aedf6aa51e Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sun, 15 Oct 2023 17:32:12 +0200 Subject: [PATCH 16/31] v2 --- crates/base-db/src/input.rs | 52 ++--- crates/cfg/src/lib.rs | 4 +- crates/project-model/src/tests.rs | 50 +++++ .../deduplication_crate_graph_A.json | 66 ++++++ .../deduplication_crate_graph_B.json | 140 ++++++++++++ crates/rust-analyzer/tests/slow-tests/main.rs | 208 +----------------- 6 files changed, 282 insertions(+), 238 deletions(-) create mode 100644 crates/project-model/test_data/deduplication_crate_graph_A.json create mode 100644 crates/project-model/test_data/deduplication_crate_graph_B.json diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index fb69e667ad95b..61a17da9aa6ef 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -329,12 +329,10 @@ pub struct CrateData { } impl CrateData { - /** - Check if [`other`] is almost equal to [`self`]. - This method has some obscure bits. These are mostly there to be compliant with - some patches. References to the patches are given. - */ - pub fn almost_eq(&self, other: &CrateData) -> bool { + /// Check if [`other`] is almost equal to [`self`] ignoring `CrateOrigin` value. + pub fn eq_ignoring_origin(&self, other: &CrateData) -> bool { + // This method has some obscure bits. These are mostly there to be compliant with + // some patches. References to the patches are given. if self.root_file_id != other.root_file_id { return false; } @@ -356,16 +354,16 @@ impl CrateData { } let mut opts = self.cfg_options.clone(); - opts.apply_diff(CfgDiff { - disable: other.cfg_options.clone().into_iter().collect(), - enable: vec![], - }); + opts.apply_diff( + CfgDiff::new(vec![], other.cfg_options.clone().into_iter().collect()) + .expect("CfgOptions were expected to contain no duplicates."), + ); let mut cfgs = opts.into_iter(); if let Some(cfg) = cfgs.next() { // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 - if !cfgs.next().is_none() || cfg.to_string() != "rust_analyzer" { + if cfgs.next().is_some() || cfg.to_string() != "rust_analyzer" { return false; } } @@ -686,41 +684,35 @@ impl CrateGraph { /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted. pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) { - enum ExtendStrategy { - Dedup(CrateId), - Replace(CrateId), - } - let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); for topo in topo { let crate_data = &mut other.arena[topo]; - crate_data.dependencies.iter_mut().for_each(|dep| { - dep.crate_id = id_map[&dep.crate_id]; - }); + crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); let res = self.arena.iter().find_map(|(id, data)| { - if data.almost_eq(crate_data) { + if data.eq_ignoring_origin(crate_data) { if data.origin.is_lib() && crate_data.origin.is_local() { // See #15656 for a relevant example. - return Some(ExtendStrategy::Replace(id)); + return Some((id, true)); } - return Some(ExtendStrategy::Dedup(id)); + return Some((id, false)); } None }); - if let Some(res) = res { - match res { - ExtendStrategy::Dedup(res) => id_map.insert(topo, res), - ExtendStrategy::Replace(res) => { - let id = self.arena.alloc(crate_data.clone()); - let _ = self.remove_and_replace(res, id); - id_map.insert(topo, id) + if let Some((res, should_update_lib_to_local)) = res { + id_map.insert(topo, res); + if should_update_lib_to_local { + let origin_old = self.arena[res].origin.clone(); + assert!(origin_old.is_lib()); + + if let CrateOrigin::Library { repo, name } = origin_old { + self.arena[res].origin = CrateOrigin::Local { repo, name: Some(name) }; } - }; + } } else { let id = self.arena.alloc(crate_data.clone()); id_map.insert(topo, id); diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 90dba008ad70e..0aeb0b0505248 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -115,8 +115,8 @@ impl<'a> IntoIterator for &'a CfgOptions { #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. - pub enable: Vec, - pub disable: Vec, + enable: Vec, + disable: Vec, } impl CfgDiff { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 7815b9dda77f2..65c6f0b256241 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -249,3 +249,53 @@ fn crate_graph_dedup() { crate_graph.extend(regex_crate_graph, &mut regex_proc_macros); assert_eq!(crate_graph.iter().count(), 118); } + +#[test] +fn test_deduplicate_crate_differing_in_origin() { + let path_map = &mut Default::default(); + let (mut crate_graph, _proc_macros) = + load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json"); + crate_graph.sort_deps(); + let (crate_graph_1, mut _proc_macros_2) = + load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json"); + + crate_graph.extend(crate_graph_1, &mut _proc_macros_2); + + let mut crates_named_p1 = vec![]; + for id in crate_graph.iter() { + let krate = &crate_graph[id]; + if let Some(name) = krate.display_name.as_ref() { + if name.to_string() == "p1" { + crates_named_p1.push(krate); + } + } + } + + assert!(crates_named_p1.len() == 1); + assert!(crates_named_p1[0].origin.is_local()); +} + +#[test] +fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { + let path_map = &mut Default::default(); + let (mut crate_graph, _proc_macros) = + load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json"); + crate_graph.sort_deps(); + let (crate_graph_1, mut _proc_macros_2) = + load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json"); + + crate_graph.extend(crate_graph_1, &mut _proc_macros_2); + + let mut crates_named_p1 = vec![]; + for id in crate_graph.iter() { + let krate = &crate_graph[id]; + if let Some(name) = krate.display_name.as_ref() { + if name.to_string() == "p1" { + crates_named_p1.push(krate); + } + } + } + + assert!(crates_named_p1.len() == 1); + assert!(crates_named_p1[0].origin.is_local()); +} diff --git a/crates/project-model/test_data/deduplication_crate_graph_A.json b/crates/project-model/test_data/deduplication_crate_graph_A.json new file mode 100644 index 0000000000000..3f627082f9f90 --- /dev/null +++ b/crates/project-model/test_data/deduplication_crate_graph_A.json @@ -0,0 +1,66 @@ +{ + "packages": [ + { + "name": "p1", + "version": "0.1.0", + "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "p1", + "src_path": "/path/to/project/projects/p1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "/path/to/project/projects/p1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + ], + "workspace_default_members": [ + "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + ], + "resolve": { + "nodes": [ + { + "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + }, + "target_directory": "/path/to/project/projects/p1/target", + "version": 1, + "workspace_root": "/path/to/project/projects/p1", + "metadata": null +} \ No newline at end of file diff --git a/crates/project-model/test_data/deduplication_crate_graph_B.json b/crates/project-model/test_data/deduplication_crate_graph_B.json new file mode 100644 index 0000000000000..a2bf1af044075 --- /dev/null +++ b/crates/project-model/test_data/deduplication_crate_graph_B.json @@ -0,0 +1,140 @@ +{ + "packages": [ + { + "name": "p1", + "version": "0.1.0", + "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "p1", + "src_path": "/path/to/project/projects/p1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "/path/to/project/projects/p1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "p2", + "version": "0.1.0", + "id": "p2 0.1.0 (path+file:///path/to/project/projects/p2)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "p1", + "source": null, + "req": "*", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "/path/to/project/projects/p1" + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "p2", + "src_path": "/path/to/project/projects/p2/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "/path/to/project/projects/p2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + ], + "workspace_default_members": [ + "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + ], + "resolve": { + "nodes": [ + { + "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "p2 0.1.0 (path+file:///path/to/project/projects/p2)", + "dependencies": [ + "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + ], + "deps": [ + { + "name": "p1", + "pkg": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + } + ], + "root": "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + }, + "target_directory": "/path/to/project/projects/p2/target", + "version": 1, + "workspace_root": "/path/to/project/projects/p2", + "metadata": null +} \ No newline at end of file diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 423c07183e25f..d59914298991c 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -21,12 +21,12 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; use lsp_types::{ notification::DidOpenTextDocument, request::{ - CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, Rename, + CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, WillRenameFiles, WorkspaceSymbolRequest, }, CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams, - PartialResultParams, Position, Range, RenameFilesParams, RenameParams, TextDocumentItem, + PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; @@ -1131,207 +1131,3 @@ version = "0.0.0" server.request::(Default::default(), json!([])); } - -#[test] -fn test_deduplicate_crate_differing_in_origin() { - let fixture = r#" -//- /projects/p1/Cargo.toml -[package] -name = "p1" -version = "0.0.0" - -//- /projects/p1/src/lib.rs -pub fn add2(left: usize, right: usize) -> usize { - left + right -} - -//- /projects/p2/Cargo.toml -[package] -name = "p2" -version = "0.0.0" - -[dependencies] -p1 = { path = "../p1" } - -//- /projects/p2/src/lib.rs -use p1::add2; - -pub fn bar() {} - "#; - - let server = Project::with_fixture(fixture) - .with_config(serde_json::json!({ - "linkedProjects" : [ - "./projects/p1/Cargo.toml", - "./projects/p2/Cargo.toml" - ], - } - )) - .with_config(serde_json::json!({ - "cargo": { "sysroot": null }, - })) - .server() - .wait_until_workspace_is_loaded(); - - let doc_id = server.doc_id("./projects/p2/src/lib.rs"); - let doc2_id = server.doc_id("./projects/p1/src/lib.rs"); - - server.request::( - RenameParams { - text_document_position: TextDocumentPositionParams { - text_document: doc_id.clone(), - position: Position { line: 0, character: 8 }, - }, - new_name: "ABC".to_owned(), - work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, - }, - json!({ - "documentChanges": [ - { - "textDocument": { - "uri": doc2_id.uri, - "version": null - }, - "edits": [ - { - "range": { - "start": { - "line": 0, - "character": 7 - }, - "end": { - "line": 0, - "character": 11 - } - }, - "newText": "ABC" - } - ] - }, - { - "textDocument": { - "uri": doc_id.uri, - "version": null - }, - "edits": [ - { - "range": { - "start": { - "line": 0, - "character": 8 - }, - "end": { - "line": 0, - "character": 12 - } - }, - "newText": "ABC" - } - ] - }, - ] - }), - ); -} - -#[test] -fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { - let fixture = r#" -//- /projects/p1/Cargo.toml -[package] -name = "p1" -version = "0.0.0" - -//- /projects/p1/src/lib.rs -pub fn add2(left: usize, right: usize) -> usize { - left + right -} - -//- /projects/p2/Cargo.toml -[package] -name = "p2" -version = "0.0.0" - -[dependencies] -p1 = { path = "../p1" } - -//- /projects/p2/src/lib.rs -use p1::add2; - -pub fn bar() {} - "#; - - let server = Project::with_fixture(fixture) - .with_config(serde_json::json!({ - "linkedProjects" : [ - "./projects/p2/Cargo.toml", - "./projects/p1/Cargo.toml", - ], - } - )) - .with_config(serde_json::json!({ - "cargo": { "sysroot": null }, - })) - .server() - .wait_until_workspace_is_loaded(); - - let doc_id = server.doc_id("./projects/p2/src/lib.rs"); - let doc2_id = server.doc_id("./projects/p1/src/lib.rs"); - - server.request::( - RenameParams { - text_document_position: TextDocumentPositionParams { - text_document: doc_id.clone(), - position: Position { line: 0, character: 8 }, - }, - new_name: "ABC".to_owned(), - work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, - }, - json!({ - "documentChanges": [ - { - "textDocument": { - "uri": doc2_id.uri, - "version": null - }, - "edits": [ - { - "range": { - "start": { - "line": 0, - "character": 7 - }, - "end": { - "line": 0, - "character": 11 - } - }, - "newText": "ABC" - } - ] - }, - { - "textDocument": { - "uri": doc_id.uri, - "version": null - }, - "edits": [ - { - "range": { - "start": { - "line": 0, - "character": 8 - }, - "end": { - "line": 0, - "character": 12 - } - }, - "newText": "ABC" - } - ] - }, - ] - }), - ); -} From 25e990d753982682c71ae09888383f2c862411c3 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 8 Nov 2023 16:51:20 +0100 Subject: [PATCH 17/31] v3 --- crates/base-db/src/input.rs | 108 +++++++++++++++--------------------- crates/cfg/src/lib.rs | 6 +- 2 files changed, 49 insertions(+), 65 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 61a17da9aa6ef..7dec01ff4deea 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -8,7 +8,7 @@ use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync}; -use cfg::{CfgDiff, CfgOptions}; +use cfg::CfgOptions; use la_arena::{Arena, Idx}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::SmolStr; @@ -330,7 +330,7 @@ pub struct CrateData { impl CrateData { /// Check if [`other`] is almost equal to [`self`] ignoring `CrateOrigin` value. - pub fn eq_ignoring_origin(&self, other: &CrateData) -> bool { + pub fn eq_ignoring_origin_and_deps(&self, other: &CrateData, ignore_dev_deps: bool) -> bool { // This method has some obscure bits. These are mostly there to be compliant with // some patches. References to the patches are given. if self.root_file_id != other.root_file_id { @@ -353,69 +353,36 @@ impl CrateData { return false; } - let mut opts = self.cfg_options.clone(); - opts.apply_diff( - CfgDiff::new(vec![], other.cfg_options.clone().into_iter().collect()) - .expect("CfgOptions were expected to contain no duplicates."), - ); - - let mut cfgs = opts.into_iter(); - if let Some(cfg) = cfgs.next() { - // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. - // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 - if cfgs.next().is_some() || cfg.to_string() != "rust_analyzer" { - return false; - } - } - - let mut itself = self.dependencies.iter(); - let mut otself = other.dependencies.iter(); - let (mut anx, mut bnx) = (itself.next(), otself.next()); - loop { - match (anx, bnx) { - (None, None) => { - break; - } - (None, Some(b)) => { - if b.kind != DependencyKind::Normal { - bnx = otself.next(); - } else { - break; - } - } - (Some(a), None) => { - if a.kind != DependencyKind::Normal { - anx = itself.next(); - } else { - break; - } - } - (Some(a), Some(b)) => { - if a.kind != DependencyKind::Normal { - anx = itself.next(); - continue; - } - - if b.kind != DependencyKind::Normal { - bnx = otself.next(); - continue; - } - - if a != b { + let mut opts = self.cfg_options.diff(&other.cfg_options).into_iter(); + match opts.len() { + 0 => (), + 1 => { + // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. + // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 + if let Some(cfg) = opts.next() { + if cfg.to_string() != "rust_analyzer" { return false; } - - anx = itself.next(); - bnx = otself.next(); } } - } + _ => return false, + }; if self.env != other.env { return false; } - true + let slf_deps = self.dependencies.iter(); + let other_deps = other.dependencies.iter(); + + if ignore_dev_deps { + slf_deps + .clone() + .filter(|it| it.kind == DependencyKind::Normal) + .eq(other_deps.clone().filter(|it| it.kind == DependencyKind::Normal)); + } + + slf_deps.eq(other_deps) } } @@ -446,7 +413,7 @@ impl Env { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum DependencyKind { Normal, Dev, @@ -480,8 +447,8 @@ impl Dependency { self.prelude } - pub fn kind(&self) -> &DependencyKind { - &self.kind + pub fn kind(&self) -> DependencyKind { + self.kind } } @@ -692,14 +659,27 @@ impl CrateGraph { crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); let res = self.arena.iter().find_map(|(id, data)| { - if data.eq_ignoring_origin(crate_data) { - if data.origin.is_lib() && crate_data.origin.is_local() { + match (&data.origin, &crate_data.origin) { + (a, b) if a == b => { + if data.eq_ignoring_origin_and_deps(&crate_data, false) { + return Some((id, false)); + } + } + (CrateOrigin::Local { .. }, CrateOrigin::Library { .. }) => { // See #15656 for a relevant example. - return Some((id, true)); + if data.eq_ignoring_origin_and_deps(&crate_data, true) { + return Some((id, false)); + } } - - return Some((id, false)); + (CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => { + // See #15656 for a relevant example. + if data.eq_ignoring_origin_and_deps(&crate_data, true) { + return Some((id, true)); + } + } + (_, _) => return None, } + None }); diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 0aeb0b0505248..f197dad2b25e2 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -7,7 +7,7 @@ mod dnf; #[cfg(test)] mod tests; -use std::fmt; +use std::{collections::HashSet, fmt}; use rustc_hash::FxHashSet; use tt::SmolStr; @@ -58,6 +58,10 @@ impl CfgOptions { self.enabled.insert(CfgAtom::KeyValue { key, value }); } + pub fn diff<'a>(&'a self, other: &'a CfgOptions) -> HashSet<&CfgAtom> { + self.enabled.difference(&other.enabled).collect() + } + pub fn apply_diff(&mut self, diff: CfgDiff) { for atom in diff.enable { self.enabled.insert(atom); From 74d8fdc8fef42fe47b12d533bb0d58ae024c4c66 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 11 Nov 2023 13:45:50 +0100 Subject: [PATCH 18/31] Update test data for crate deduping Make data reflect a case where dev deps are existent. base-db::CrateGraph::extend now adds dev dependencies for a crate in case of its upgrading from a CrateOrigin::Lib kind of a crate to a CrateOrigin::Local one. --- crates/base-db/src/input.rs | 51 +++++++--- crates/project-model/src/tests.rs | 10 +- .../deduplication_crate_graph_A.json | 94 +++++++++++++++++-- .../deduplication_crate_graph_B.json | 48 ++++++---- 4 files changed, 162 insertions(+), 41 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 7dec01ff4deea..cec3e91eee47d 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -376,7 +376,7 @@ impl CrateData { let other_deps = other.dependencies.iter(); if ignore_dev_deps { - slf_deps + return slf_deps .clone() .filter(|it| it.kind == DependencyKind::Normal) .eq(other_deps.clone().filter(|it| it.kind == DependencyKind::Normal)); @@ -524,7 +524,7 @@ impl CrateGraph { self.check_cycle_after_dependency(from, dep.crate_id)?; - self.arena[from].add_dep(dep); + self.arena[from].add_dep_unchecked(dep); Ok(()) } @@ -665,16 +665,11 @@ impl CrateGraph { return Some((id, false)); } } - (CrateOrigin::Local { .. }, CrateOrigin::Library { .. }) => { + (a @ CrateOrigin::Local { .. }, CrateOrigin::Library { .. }) + | (a @ CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => { // See #15656 for a relevant example. if data.eq_ignoring_origin_and_deps(&crate_data, true) { - return Some((id, false)); - } - } - (CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => { - // See #15656 for a relevant example. - if data.eq_ignoring_origin_and_deps(&crate_data, true) { - return Some((id, true)); + return Some((id, if a.is_local() { false } else { true })); } } (_, _) => return None, @@ -692,6 +687,16 @@ impl CrateGraph { if let CrateOrigin::Library { repo, name } = origin_old { self.arena[res].origin = CrateOrigin::Local { repo, name: Some(name) }; } + + // Move local's dev dependencies into the newly-local-formerly-lib crate. + let dev_deps = crate_data + .dependencies + .clone() + .into_iter() + .filter(|dep| dep.kind() == DependencyKind::Dev) + .collect::>(); + + self.arena[res].add_dep(dev_deps).unwrap_or_default(); } } else { let id = self.arena.alloc(crate_data.clone()); @@ -761,10 +766,34 @@ impl ops::Index for CrateGraph { } } +struct ExistingDepsError(Vec); + impl CrateData { - fn add_dep(&mut self, dep: Dependency) { + /// Add a dependency to `self` without checking if the dependency + // is existent among `self.dependencies`. + fn add_dep_unchecked(&mut self, dep: Dependency) { self.dependencies.push(dep) } + + /// Add `deps` to `self` if the dependency is not already listed. + /// Finally returning an `Err` propagating the dependencies it couldn't add. + fn add_dep(&mut self, deps: Vec) -> Result<(), ExistingDepsError> { + let mut existing_deps: Vec = vec![]; + + deps.into_iter().for_each(|dep| { + if !self.dependencies.contains(&dep) { + self.dependencies.push(dep); + } else { + existing_deps.push(dep); + } + }); + + if !existing_deps.is_empty() { + return Err(ExistingDepsError(existing_deps)); + } + + Ok(()) + } } impl FromStr for Edition { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 65c6f0b256241..35ac80eee328a 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use base_db::{CrateGraph, FileId, ProcMacroPaths}; +use base_db::{CrateGraph, DependencyKind, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; use paths::{AbsPath, AbsPathBuf}; @@ -272,7 +272,9 @@ fn test_deduplicate_crate_differing_in_origin() { } assert!(crates_named_p1.len() == 1); - assert!(crates_named_p1[0].origin.is_local()); + let p1 = crates_named_p1[0]; + assert!(p1.dependencies.iter().filter(|dep| dep.kind() == DependencyKind::Dev).count() == 1); + assert!(p1.origin.is_local()); } #[test] @@ -297,5 +299,7 @@ fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { } assert!(crates_named_p1.len() == 1); - assert!(crates_named_p1[0].origin.is_local()); + let p1 = crates_named_p1[0]; + assert!(p1.dependencies.iter().filter(|dep| dep.kind() == DependencyKind::Dev).count() == 1); + assert!(p1.origin.is_local()); } diff --git a/crates/project-model/test_data/deduplication_crate_graph_A.json b/crates/project-model/test_data/deduplication_crate_graph_A.json index 3f627082f9f90..edaf185fc61a8 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_A.json +++ b/crates/project-model/test_data/deduplication_crate_graph_A.json @@ -3,12 +3,26 @@ { "name": "p1", "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", "license": null, "license_file": null, "description": null, "source": null, - "dependencies": [], + "dependencies": [ + { + "name": "p3", + "source": null, + "req": "*", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "/path/to/project/example_project/projects/p3" + } + ], "targets": [ { "kind": [ @@ -18,7 +32,7 @@ "lib" ], "name": "p1", - "src_path": "/path/to/project/projects/p1/src/lib.rs", + "src_path": "/path/to/project/example_project/projects/p1/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -26,7 +40,48 @@ } ], "features": {}, - "manifest_path": "/path/to/project/projects/p1/Cargo.toml", + "manifest_path": "/path/to/project/example_project/projects/p1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "p3", + "version": "0.1.0", + "id": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "p3", + "src_path": "/path/to/project/example_project/projects/p3/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "/path/to/project/example_project/projects/p3/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -43,24 +98,43 @@ } ], "workspace_members": [ - "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" ], "workspace_default_members": [ - "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" ], "resolve": { "nodes": [ { - "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", + "dependencies": [ + "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)" + ], + "deps": [ + { + "name": "p3", + "pkg": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", "dependencies": [], "deps": [], "features": [] } ], - "root": "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + "root": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" }, - "target_directory": "/path/to/project/projects/p1/target", + "target_directory": "/path/to/project/example_project/projects/p1/target", "version": 1, - "workspace_root": "/path/to/project/projects/p1", + "workspace_root": "/path/to/project/example_project/projects/p1", "metadata": null } \ No newline at end of file diff --git a/crates/project-model/test_data/deduplication_crate_graph_B.json b/crates/project-model/test_data/deduplication_crate_graph_B.json index a2bf1af044075..4f753db71bf00 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_B.json +++ b/crates/project-model/test_data/deduplication_crate_graph_B.json @@ -3,12 +3,26 @@ { "name": "p1", "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", "license": null, "license_file": null, "description": null, "source": null, - "dependencies": [], + "dependencies": [ + { + "name": "p3", + "source": null, + "req": "*", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "/path/to/project/example_project/projects/p3" + } + ], "targets": [ { "kind": [ @@ -18,7 +32,7 @@ "lib" ], "name": "p1", - "src_path": "/path/to/project/projects/p1/src/lib.rs", + "src_path": "/path/to/project/example_project/projects/p1/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -26,7 +40,7 @@ } ], "features": {}, - "manifest_path": "/path/to/project/projects/p1/Cargo.toml", + "manifest_path": "/path/to/project/example_project/projects/p1/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -44,7 +58,7 @@ { "name": "p2", "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///path/to/project/projects/p2)", + "id": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)", "license": null, "license_file": null, "description": null, @@ -61,7 +75,7 @@ "features": [], "target": null, "registry": null, - "path": "/path/to/project/projects/p1" + "path": "/path/to/project/example_project/projects/p1" } ], "targets": [ @@ -73,7 +87,7 @@ "lib" ], "name": "p2", - "src_path": "/path/to/project/projects/p2/src/lib.rs", + "src_path": "/path/to/project/example_project/projects/p2/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -81,7 +95,7 @@ } ], "features": {}, - "manifest_path": "/path/to/project/projects/p2/Cargo.toml", + "manifest_path": "/path/to/project/example_project/projects/p2/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -98,28 +112,28 @@ } ], "workspace_members": [ - "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" ], "workspace_default_members": [ - "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" ], "resolve": { "nodes": [ { - "id": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", "dependencies": [], "deps": [], "features": [] }, { - "id": "p2 0.1.0 (path+file:///path/to/project/projects/p2)", + "id": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)", "dependencies": [ - "p1 0.1.0 (path+file:///path/to/project/projects/p1)" + "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" ], "deps": [ { "name": "p1", - "pkg": "p1 0.1.0 (path+file:///path/to/project/projects/p1)", + "pkg": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", "dep_kinds": [ { "kind": null, @@ -131,10 +145,10 @@ "features": [] } ], - "root": "p2 0.1.0 (path+file:///path/to/project/projects/p2)" + "root": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" }, - "target_directory": "/path/to/project/projects/p2/target", + "target_directory": "/path/to/project/example_project/projects/p2/target", "version": 1, - "workspace_root": "/path/to/project/projects/p2", + "workspace_root": "/path/to/project/example_project/projects/p2", "metadata": null } \ No newline at end of file From f79e8182c1e611b385fd91c924e347ea5f6c71d3 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Tue, 21 Nov 2023 14:05:31 +0100 Subject: [PATCH 19/31] v3 --- crates/base-db/src/input.rs | 79 ++++++++++++------------------------- crates/cfg/src/lib.rs | 9 +++-- 2 files changed, 31 insertions(+), 57 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index cec3e91eee47d..82149892bd688 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -353,20 +353,18 @@ impl CrateData { return false; } - let mut opts = self.cfg_options.diff(&other.cfg_options).into_iter(); - match opts.len() { - 0 => (), - 1 => { - // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. - // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 - if let Some(cfg) = opts.next() { - if cfg.to_string() != "rust_analyzer" { - return false; - } - } + let mut opts = self.cfg_options.difference(&other.cfg_options); + if let Some(it) = opts.next() { + // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. + // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 + if it.to_string() != "rust_analyzer" { + return false; } - _ => return false, - }; + + if let Some(_) = opts.next() { + return false; + } + } if self.env != other.env { return false; @@ -378,8 +376,8 @@ impl CrateData { if ignore_dev_deps { return slf_deps .clone() - .filter(|it| it.kind == DependencyKind::Normal) - .eq(other_deps.clone().filter(|it| it.kind == DependencyKind::Normal)); + .filter(|it| it.kind != DependencyKind::Dev) + .eq(other_deps.clone().filter(|it| it.kind != DependencyKind::Dev)); } slf_deps.eq(other_deps) @@ -524,7 +522,7 @@ impl CrateGraph { self.check_cycle_after_dependency(from, dep.crate_id)?; - self.arena[from].add_dep_unchecked(dep); + self.arena[from].add_dep(dep); Ok(()) } @@ -667,7 +665,12 @@ impl CrateGraph { } (a @ CrateOrigin::Local { .. }, CrateOrigin::Library { .. }) | (a @ CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => { - // See #15656 for a relevant example. + // If the origins differ, check if the two crates are equal without + // considering the dev dependencies, if they are, they most likely are in + // different loaded workspaces which may cause issues. We keep the local + // version and discard the library one as the local version may have + // dev-dependencies that we want to keep resolving. See #15656 for more + // information. if data.eq_ignoring_origin_and_deps(&crate_data, true) { return Some((id, if a.is_local() { false } else { true })); } @@ -681,22 +684,12 @@ impl CrateGraph { if let Some((res, should_update_lib_to_local)) = res { id_map.insert(topo, res); if should_update_lib_to_local { - let origin_old = self.arena[res].origin.clone(); - assert!(origin_old.is_lib()); - - if let CrateOrigin::Library { repo, name } = origin_old { - self.arena[res].origin = CrateOrigin::Local { repo, name: Some(name) }; - } + assert!(self.arena[res].origin.is_lib()); + assert!(crate_data.origin.is_local()); + self.arena[res].origin = crate_data.origin.clone(); // Move local's dev dependencies into the newly-local-formerly-lib crate. - let dev_deps = crate_data - .dependencies - .clone() - .into_iter() - .filter(|dep| dep.kind() == DependencyKind::Dev) - .collect::>(); - - self.arena[res].add_dep(dev_deps).unwrap_or_default(); + self.arena[res].dependencies = crate_data.dependencies.clone(); } } else { let id = self.arena.alloc(crate_data.clone()); @@ -766,34 +759,12 @@ impl ops::Index for CrateGraph { } } -struct ExistingDepsError(Vec); - impl CrateData { /// Add a dependency to `self` without checking if the dependency // is existent among `self.dependencies`. - fn add_dep_unchecked(&mut self, dep: Dependency) { + fn add_dep(&mut self, dep: Dependency) { self.dependencies.push(dep) } - - /// Add `deps` to `self` if the dependency is not already listed. - /// Finally returning an `Err` propagating the dependencies it couldn't add. - fn add_dep(&mut self, deps: Vec) -> Result<(), ExistingDepsError> { - let mut existing_deps: Vec = vec![]; - - deps.into_iter().for_each(|dep| { - if !self.dependencies.contains(&dep) { - self.dependencies.push(dep); - } else { - existing_deps.push(dep); - } - }); - - if !existing_deps.is_empty() { - return Err(ExistingDepsError(existing_deps)); - } - - Ok(()) - } } impl FromStr for Edition { diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index f197dad2b25e2..8bbe5e2a8c29b 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -7,7 +7,7 @@ mod dnf; #[cfg(test)] mod tests; -use std::{collections::HashSet, fmt}; +use std::fmt; use rustc_hash::FxHashSet; use tt::SmolStr; @@ -58,8 +58,11 @@ impl CfgOptions { self.enabled.insert(CfgAtom::KeyValue { key, value }); } - pub fn diff<'a>(&'a self, other: &'a CfgOptions) -> HashSet<&CfgAtom> { - self.enabled.difference(&other.enabled).collect() + pub fn difference<'a>( + &'a self, + other: &'a CfgOptions, + ) -> impl Iterator + 'a { + self.enabled.difference(&other.enabled) } pub fn apply_diff(&mut self, diff: CfgDiff) { From 736994f02664aa4cd84f55be481aae33bcf48ca3 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 23 Nov 2023 02:08:30 +0100 Subject: [PATCH 20/31] Make test cases simpler --- crates/base-db/src/input.rs | 2 +- crates/project-model/src/tests.rs | 32 +++--- .../deduplication_crate_graph_A.json | 44 +++---- .../deduplication_crate_graph_B.json | 108 ++---------------- 4 files changed, 48 insertions(+), 138 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 82149892bd688..e4f78321e215a 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -668,7 +668,7 @@ impl CrateGraph { // If the origins differ, check if the two crates are equal without // considering the dev dependencies, if they are, they most likely are in // different loaded workspaces which may cause issues. We keep the local - // version and discard the library one as the local version may have + // version and discard the library one as the local version may have // dev-dependencies that we want to keep resolving. See #15656 for more // information. if data.eq_ignoring_origin_and_deps(&crate_data, true) { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 35ac80eee328a..98f3063bb9829 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use base_db::{CrateGraph, DependencyKind, FileId, ProcMacroPaths}; +use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; use paths::{AbsPath, AbsPathBuf}; @@ -251,7 +251,7 @@ fn crate_graph_dedup() { } #[test] -fn test_deduplicate_crate_differing_in_origin() { +fn test_deduplicate_origin_dev() { let path_map = &mut Default::default(); let (mut crate_graph, _proc_macros) = load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json"); @@ -261,24 +261,23 @@ fn test_deduplicate_crate_differing_in_origin() { crate_graph.extend(crate_graph_1, &mut _proc_macros_2); - let mut crates_named_p1 = vec![]; + let mut crates_named_p2 = vec![]; for id in crate_graph.iter() { let krate = &crate_graph[id]; if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p1" { - crates_named_p1.push(krate); + if name.to_string() == "p2" { + crates_named_p2.push(krate); } } } - assert!(crates_named_p1.len() == 1); - let p1 = crates_named_p1[0]; - assert!(p1.dependencies.iter().filter(|dep| dep.kind() == DependencyKind::Dev).count() == 1); - assert!(p1.origin.is_local()); + assert!(crates_named_p2.len() == 1); + let p2 = crates_named_p2[0]; + assert!(p2.origin.is_local()); } #[test] -fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { +fn test_deduplicate_origin_dev_rev() { let path_map = &mut Default::default(); let (mut crate_graph, _proc_macros) = load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json"); @@ -288,18 +287,17 @@ fn test_deduplicate_crate_differing_in_origin_in_rev_resolution_order() { crate_graph.extend(crate_graph_1, &mut _proc_macros_2); - let mut crates_named_p1 = vec![]; + let mut crates_named_p2 = vec![]; for id in crate_graph.iter() { let krate = &crate_graph[id]; if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p1" { - crates_named_p1.push(krate); + if name.to_string() == "p2" { + crates_named_p2.push(krate); } } } - assert!(crates_named_p1.len() == 1); - let p1 = crates_named_p1[0]; - assert!(p1.dependencies.iter().filter(|dep| dep.kind() == DependencyKind::Dev).count() == 1); - assert!(p1.origin.is_local()); + assert!(crates_named_p2.len() == 1); + let p2 = crates_named_p2[0]; + assert!(p2.origin.is_local()); } diff --git a/crates/project-model/test_data/deduplication_crate_graph_A.json b/crates/project-model/test_data/deduplication_crate_graph_A.json index edaf185fc61a8..66bfcb2bf6efa 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_A.json +++ b/crates/project-model/test_data/deduplication_crate_graph_A.json @@ -3,24 +3,24 @@ { "name": "p1", "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", + "id": "p1 0.1.0 (path+file:///example_project/p1)", "license": null, "license_file": null, "description": null, "source": null, "dependencies": [ { - "name": "p3", + "name": "p2", "source": null, "req": "*", - "kind": "dev", + "kind": null, "rename": null, "optional": false, "uses_default_features": true, "features": [], "target": null, "registry": null, - "path": "/path/to/project/example_project/projects/p3" + "path": "/example_project/p2" } ], "targets": [ @@ -32,7 +32,7 @@ "lib" ], "name": "p1", - "src_path": "/path/to/project/example_project/projects/p1/src/lib.rs", + "src_path": "/example_project/p1/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -40,7 +40,7 @@ } ], "features": {}, - "manifest_path": "/path/to/project/example_project/projects/p1/Cargo.toml", + "manifest_path": "/example_project/p1/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -56,9 +56,9 @@ "rust_version": null }, { - "name": "p3", + "name": "p2", "version": "0.1.0", - "id": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", + "id": "p2 0.1.0 (path+file:///example_project/p2)", "license": null, "license_file": null, "description": null, @@ -72,8 +72,8 @@ "crate_types": [ "lib" ], - "name": "p3", - "src_path": "/path/to/project/example_project/projects/p3/src/lib.rs", + "name": "p2", + "src_path": "/example_project/p2/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -81,7 +81,7 @@ } ], "features": {}, - "manifest_path": "/path/to/project/example_project/projects/p3/Cargo.toml", + "manifest_path": "/example_project/p2/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -98,25 +98,25 @@ } ], "workspace_members": [ - "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" + "p1 0.1.0 (path+file:///example_project/p1)" ], "workspace_default_members": [ - "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" + "p1 0.1.0 (path+file:///example_project/p1)" ], "resolve": { "nodes": [ { - "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", + "id": "p1 0.1.0 (path+file:///example_project/p1)", "dependencies": [ - "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)" + "p2 0.1.0 (path+file:///example_project/p2)" ], "deps": [ { - "name": "p3", - "pkg": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", + "name": "p2", + "pkg": "p2 0.1.0 (path+file:///example_project/p2)", "dep_kinds": [ { - "kind": "dev", + "kind": null, "target": null } ] @@ -125,16 +125,16 @@ "features": [] }, { - "id": "p3 0.1.0 (path+file:///path/to/project/example_project/projects/p3)", + "id": "p2 0.1.0 (path+file:///example_project/p2)", "dependencies": [], "deps": [], "features": [] } ], - "root": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" + "root": "p1 0.1.0 (path+file:///example_project/p1)" }, - "target_directory": "/path/to/project/example_project/projects/p1/target", + "target_directory": "/example_project/p1/target", "version": 1, - "workspace_root": "/path/to/project/example_project/projects/p1", + "workspace_root": "/example_project/p1", "metadata": null } \ No newline at end of file diff --git a/crates/project-model/test_data/deduplication_crate_graph_B.json b/crates/project-model/test_data/deduplication_crate_graph_B.json index 4f753db71bf00..6e67da7eae136 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_B.json +++ b/crates/project-model/test_data/deduplication_crate_graph_B.json @@ -1,83 +1,14 @@ { "packages": [ - { - "name": "p1", - "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [ - { - "name": "p3", - "source": null, - "req": "*", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "/path/to/project/example_project/projects/p3" - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p1", - "src_path": "/path/to/project/example_project/projects/p1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "/path/to/project/example_project/projects/p1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - }, { "name": "p2", "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)", + "id": "p2 0.1.0 (path+file:///example_project/p2)", "license": null, "license_file": null, "description": null, "source": null, - "dependencies": [ - { - "name": "p1", - "source": null, - "req": "*", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "/path/to/project/example_project/projects/p1" - } - ], + "dependencies": [], "targets": [ { "kind": [ @@ -87,7 +18,7 @@ "lib" ], "name": "p2", - "src_path": "/path/to/project/example_project/projects/p2/src/lib.rs", + "src_path": "/example_project/p2/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -95,7 +26,7 @@ } ], "features": {}, - "manifest_path": "/path/to/project/example_project/projects/p2/Cargo.toml", + "manifest_path": "/example_project/p2/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -112,43 +43,24 @@ } ], "workspace_members": [ - "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" + "p2 0.1.0 (path+file:///example_project/p2)" ], "workspace_default_members": [ - "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" + "p2 0.1.0 (path+file:///example_project/p2)" ], "resolve": { "nodes": [ { - "id": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", + "id": "p2 0.1.0 (path+file:///example_project/p2)", "dependencies": [], "deps": [], "features": [] - }, - { - "id": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)", - "dependencies": [ - "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)" - ], - "deps": [ - { - "name": "p1", - "pkg": "p1 0.1.0 (path+file:///path/to/project/example_project/projects/p1)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] } ], - "root": "p2 0.1.0 (path+file:///path/to/project/example_project/projects/p2)" + "root": "p2 0.1.0 (path+file:///example_project/p2)" }, - "target_directory": "/path/to/project/example_project/projects/p2/target", + "target_directory": "/example_project/p2/target", "version": 1, - "workspace_root": "/path/to/project/example_project/projects/p2", + "workspace_root": "/example_project/p2", "metadata": null } \ No newline at end of file From ba1b08080559fdaa714d9e58ce21f7f2030adc56 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 23 Nov 2023 12:34:38 +0100 Subject: [PATCH 21/31] Precede paths with $ROOT$ --- .../test_data/deduplication_crate_graph_A.json | 14 +++++++------- .../test_data/deduplication_crate_graph_B.json | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/project-model/test_data/deduplication_crate_graph_A.json b/crates/project-model/test_data/deduplication_crate_graph_A.json index 66bfcb2bf6efa..b0fb5845cef76 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_A.json +++ b/crates/project-model/test_data/deduplication_crate_graph_A.json @@ -20,7 +20,7 @@ "features": [], "target": null, "registry": null, - "path": "/example_project/p2" + "path": "$ROOT$example_project/p2" } ], "targets": [ @@ -32,7 +32,7 @@ "lib" ], "name": "p1", - "src_path": "/example_project/p1/src/lib.rs", + "src_path": "$ROOT$example_project/p1/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -40,7 +40,7 @@ } ], "features": {}, - "manifest_path": "/example_project/p1/Cargo.toml", + "manifest_path": "$ROOT$example_project/p1/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -73,7 +73,7 @@ "lib" ], "name": "p2", - "src_path": "/example_project/p2/src/lib.rs", + "src_path": "$ROOT$example_project/p2/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -81,7 +81,7 @@ } ], "features": {}, - "manifest_path": "/example_project/p2/Cargo.toml", + "manifest_path": "$ROOT$example_project/p2/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -133,8 +133,8 @@ ], "root": "p1 0.1.0 (path+file:///example_project/p1)" }, - "target_directory": "/example_project/p1/target", + "target_directory": "$ROOT$example_project/p1/target", "version": 1, - "workspace_root": "/example_project/p1", + "workspace_root": "$ROOT$example_project/p1", "metadata": null } \ No newline at end of file diff --git a/crates/project-model/test_data/deduplication_crate_graph_B.json b/crates/project-model/test_data/deduplication_crate_graph_B.json index 6e67da7eae136..b5d1e16e62e03 100644 --- a/crates/project-model/test_data/deduplication_crate_graph_B.json +++ b/crates/project-model/test_data/deduplication_crate_graph_B.json @@ -18,7 +18,7 @@ "lib" ], "name": "p2", - "src_path": "/example_project/p2/src/lib.rs", + "src_path": "$ROOT$example_project/p2/src/lib.rs", "edition": "2021", "doc": true, "doctest": true, @@ -26,7 +26,7 @@ } ], "features": {}, - "manifest_path": "/example_project/p2/Cargo.toml", + "manifest_path": "$ROOT$example_project/p2/Cargo.toml", "metadata": null, "publish": null, "authors": [], @@ -59,8 +59,8 @@ ], "root": "p2 0.1.0 (path+file:///example_project/p2)" }, - "target_directory": "/example_project/p2/target", + "target_directory": "$ROOT$example_project/p2/target", "version": 1, - "workspace_root": "/example_project/p2", + "workspace_root": "$ROOT$example_project/p2", "metadata": null } \ No newline at end of file From 615abb3c92430e85a9b197022e008ef129bdb45f Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Fri, 24 Nov 2023 12:57:16 +0200 Subject: [PATCH 22/31] Improve completion label details display --- crates/ide-completion/src/item.rs | 21 +++++++++++++++++---- crates/rust-analyzer/src/lsp/to_proto.rs | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index ed74ef7b667bb..5397a9fc90b95 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -26,6 +26,10 @@ use crate::{ pub struct CompletionItem { /// Label in the completion pop up which identifies completion. pub label: SmolStr, + /// Addition label details in the completion pop up that are + /// displayed and aligned on the right side after the label. + pub label_detail: Option, + /// Range of identifier that is being completed. /// /// It should be used primarily for UI, but we also use this to convert @@ -425,13 +429,14 @@ impl Builder { pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem { let _p = profile::span("item::Builder::build"); - let mut label = self.label; + let label = self.label; + let mut label_detail = None; let mut lookup = self.lookup.unwrap_or_else(|| label.clone()); let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); if !self.doc_aliases.is_empty() { let doc_aliases = self.doc_aliases.iter().join(", "); - label = SmolStr::from(format!("{label} (alias {doc_aliases})")); + label_detail.replace(SmolStr::from(format!(" (alias {doc_aliases})"))); let lookup_doc_aliases = self .doc_aliases .iter() @@ -454,10 +459,17 @@ impl Builder { if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one if let Some(original_path) = import_edit.original_path.as_ref() { - label = SmolStr::from(format!("{label} (use {})", original_path.display(db))); + label_detail.replace(SmolStr::from(format!( + "{} (use {})", + label_detail.as_deref().unwrap_or_default(), + original_path.display(db) + ))); } } else if let Some(trait_name) = self.trait_name { - label = SmolStr::from(format!("{label} (as {trait_name})")); + label_detail.replace(SmolStr::from(format!( + "{} (as {trait_name})", + label_detail.as_deref().unwrap_or_default(), + ))); } let text_edit = match self.text_edit { @@ -479,6 +491,7 @@ impl Builder { CompletionItem { source_range: self.source_range, label, + label_detail, text_edit, is_snippet: self.is_snippet, detail: self.detail, diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 4e3dce35ec396..b4ba5bf4f91ac 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -301,7 +301,7 @@ fn completion_item( if config.completion_label_details_support() { lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails { - detail: None, + detail: item.label_detail.as_ref().map(ToString::to_string), description: lsp_item.detail.clone(), }); } From f0adf8c4ecedae38796769bd9ef3d4daa5ec2260 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Fri, 24 Nov 2023 13:28:30 +0200 Subject: [PATCH 23/31] Update tests --- crates/ide-completion/src/render.rs | 6 +++++- crates/ide-completion/src/tests.rs | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index dfe8fe7e2f7f7..00a9081985b83 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -557,7 +557,11 @@ mod tests { let tag = it.kind.tag(); let relevance = display_relevance(it.relevance); - items.push(format!("{tag} {} {relevance}\n", it.label)); + items.push(format!( + "{tag} {}{} {relevance}\n", + it.label, + it.label_detail.clone().unwrap_or_default(), + )); if let Some((label, _indel, relevance)) = it.ref_match() { let relevance = display_relevance(relevance); diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 9db8e972dd4ba..f28afacc586ff 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -150,16 +150,29 @@ fn render_completion_list(completions: Vec) -> String { fn monospace_width(s: &str) -> usize { s.chars().count() } - let label_width = - completions.iter().map(|it| monospace_width(&it.label)).max().unwrap_or_default().min(22); + let label_width = completions + .iter() + .map(|it| { + monospace_width(&it.label) + + monospace_width(it.label_detail.as_deref().unwrap_or_default()) + }) + .max() + .unwrap_or_default() + .min(22); completions .into_iter() .map(|it| { let tag = it.kind.tag(); let var_name = format!("{tag} {}", it.label); let mut buf = var_name; + if let Some(ref label_detail) = it.label_detail { + format_to!(buf, "{label_detail}"); + } if let Some(detail) = it.detail { - let width = label_width.saturating_sub(monospace_width(&it.label)); + let width = label_width.saturating_sub( + monospace_width(&it.label) + + monospace_width(&it.label_detail.unwrap_or_default()), + ); format_to!(buf, "{:width$} {}", "", detail, width = width); } if it.deprecated { From b68f5311b52d109cf813dff1268264d17af766ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 24 Nov 2023 09:06:43 -0800 Subject: [PATCH 24/31] Replace `option.map(cond) == Some(true)` with `option.is_some_and(cond)` --- crates/ide-assists/src/handlers/toggle_ignore.rs | 2 +- crates/ide/src/extend_selection.rs | 2 +- crates/rust-analyzer/tests/slow-tests/tidy.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/toggle_ignore.rs b/crates/ide-assists/src/handlers/toggle_ignore.rs index b7d57f02be566..f864ee50c81da 100644 --- a/crates/ide-assists/src/handlers/toggle_ignore.rs +++ b/crates/ide-assists/src/handlers/toggle_ignore.rs @@ -55,7 +55,7 @@ pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } fn has_ignore_attribute(fn_def: &ast::Fn) -> Option { - fn_def.attrs().find(|attr| attr.path().map(|it| it.syntax().text() == "ignore") == Some(true)) + fn_def.attrs().find(|attr| attr.path().is_some_and(|it| it.syntax().text() == "ignore")) } #[cfg(test)] diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 3d89599c5832e..9b2ff070c74b1 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs @@ -108,7 +108,7 @@ fn try_extend_selection( let node = shallowest_node(&node); - if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { + if node.parent().is_some_and(|n| list_kinds.contains(&n.kind())) { if let Some(range) = extend_list_item(&node) { return Some(range); } diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 56b5fcef3c2f5..45adbf5c573be 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -316,7 +316,7 @@ fn check_trailing_ws(path: &Path, text: &str) { return; } for (line_number, line) in text.lines().enumerate() { - if line.chars().last().map(char::is_whitespace) == Some(true) { + if line.chars().last().is_some_and(char::is_whitespace) { panic!("Trailing whitespace in {} at line {}", path.display(), line_number + 1) } } From 94cea4663a3a826beccfeb291187df4849fe990e Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Fri, 24 Nov 2023 15:24:57 +0200 Subject: [PATCH 25/31] fix: add fallback for completion label details --- crates/ide-completion/src/item.rs | 2 +- crates/rust-analyzer/src/lsp/to_proto.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 5397a9fc90b95..99b895eed4d23 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -26,7 +26,7 @@ use crate::{ pub struct CompletionItem { /// Label in the completion pop up which identifies completion. pub label: SmolStr, - /// Addition label details in the completion pop up that are + /// Additional label details in the completion pop up that are /// displayed and aligned on the right side after the label. pub label_detail: Option, diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index b4ba5bf4f91ac..fb366fd5cc41f 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -304,6 +304,8 @@ fn completion_item( detail: item.label_detail.as_ref().map(ToString::to_string), description: lsp_item.detail.clone(), }); + } else if let Some(label_detail) = item.label_detail { + lsp_item.label.push_str(label_detail.as_str()); } set_score(&mut lsp_item, max_relevance, item.relevance); From fa74ac845710c9ca233a0a89ca5e0786a11d08e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 25 Nov 2023 20:35:26 +0200 Subject: [PATCH 26/31] Bump ra-ap-rustc_parse_format --- Cargo.lock | 37 +++++++++++++++++++--------- crates/rustc-dependencies/Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11283034ea2fb..a1cb09bafaada 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1469,9 +1469,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.14.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643ca3609870b1778d9cd1f2a8e4ccb4af0f48f3637cc257a09494d087bd93dc" +checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" dependencies = [ "arrayvec", "smallvec", @@ -1479,19 +1479,32 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" +checksum = "69fb5da07e1a39222d9c311203123c3b6a86420fa06dc695aa1661b0aecf8d16" dependencies = [ "arrayvec", + "ra-ap-rustc_index_macros", "smallvec", ] +[[package]] +name = "ra-ap-rustc_index_macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d69f9f6af58124f2da0cb8b0c3d8494e0d883a5fe0c6732258bde81ac5a87cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "synstructure", +] + [[package]] name = "ra-ap-rustc_lexer" -version = "0.14.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ffd24f9ba4f1d25ff27ca1469b8d22a3bdfb12cf644fc8bfcb63121fa5da6b" +checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" dependencies = [ "unicode-properties", "unicode-xid", @@ -1499,9 +1512,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" +checksum = "9d5e8650195795c4023d8321846466994a975bc457cb8a91c0b3b17a5fc8ba40" dependencies = [ "unicode-properties", "unicode-xid", @@ -1509,12 +1522,12 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.14.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207b5ac1a21d4926695e03b605ffb9f63d4968e0488e9197c04c512c37303aa7" +checksum = "0a6b325ee1ec90e4dbd4394913adf4ef32e4fcf2b311ec9563a0fa50cd549af6" dependencies = [ - "ra-ap-rustc_index 0.14.0", - "ra-ap-rustc_lexer 0.14.0", + "ra-ap-rustc_index 0.20.0", + "ra-ap-rustc_lexer 0.20.0", ] [[package]] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index ba36fb0b04464..893ed76e3f1e9 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -12,7 +12,7 @@ authors.workspace = true [dependencies] ra-ap-rustc_lexer = { version = "0.19.0" } -ra-ap-rustc_parse_format = { version = "0.14.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.20.0", default-features = false } ra-ap-rustc_index = { version = "0.19.0", default-features = false } ra-ap-rustc_abi = { version = "0.19.0", default-features = false } From c01679eb7b2d2fb9c2c0899a5fe5898581f39fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 25 Nov 2023 20:36:06 +0200 Subject: [PATCH 27/31] Bump ra-ap-rustc_lexer --- Cargo.lock | 14 ++------------ crates/rustc-dependencies/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1cb09bafaada..5189811f49532 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1500,16 +1500,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "ra-ap-rustc_lexer" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" -dependencies = [ - "unicode-properties", - "unicode-xid", -] - [[package]] name = "ra-ap-rustc_lexer" version = "0.20.0" @@ -1527,7 +1517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6b325ee1ec90e4dbd4394913adf4ef32e4fcf2b311ec9563a0fa50cd549af6" dependencies = [ "ra-ap-rustc_index 0.20.0", - "ra-ap-rustc_lexer 0.20.0", + "ra-ap-rustc_lexer", ] [[package]] @@ -1650,7 +1640,7 @@ version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", "ra-ap-rustc_index 0.19.0", - "ra-ap-rustc_lexer 0.19.0", + "ra-ap-rustc_lexer", "ra-ap-rustc_parse_format", ] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index 893ed76e3f1e9..41bcfd11722c4 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -11,7 +11,7 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ra-ap-rustc_lexer = { version = "0.19.0" } +ra-ap-rustc_lexer = { version = "0.20.0" } ra-ap-rustc_parse_format = { version = "0.20.0", default-features = false } ra-ap-rustc_index = { version = "0.19.0", default-features = false } ra-ap-rustc_abi = { version = "0.19.0", default-features = false } From aadeb66d036d4a3d786f3ee3eac5cd5aab73e180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 25 Nov 2023 20:36:45 +0200 Subject: [PATCH 28/31] Bump ra-ap-rustc_index and ra-ap-rustc_abi --- Cargo.lock | 20 +++++--------------- crates/rustc-dependencies/Cargo.toml | 4 ++-- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5189811f49532..29644efb07c69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1458,25 +1458,15 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2ea80a299f04a896000ce17b76f3aa1d2fe59f347fbc99c4b8970316ef5a0d" +checksum = "b5f38444d48da534b3bb612713fce9b0aeeffb2e0dfa242764f55482acc5b52d" dependencies = [ "bitflags 1.3.2", - "ra-ap-rustc_index 0.19.0", + "ra-ap-rustc_index", "tracing", ] -[[package]] -name = "ra-ap-rustc_index" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" -dependencies = [ - "arrayvec", - "smallvec", -] - [[package]] name = "ra-ap-rustc_index" version = "0.20.0" @@ -1516,7 +1506,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6b325ee1ec90e4dbd4394913adf4ef32e4fcf2b311ec9563a0fa50cd549af6" dependencies = [ - "ra-ap-rustc_index 0.20.0", + "ra-ap-rustc_index", "ra-ap-rustc_lexer", ] @@ -1639,7 +1629,7 @@ name = "rustc-dependencies" version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", - "ra-ap-rustc_index 0.19.0", + "ra-ap-rustc_index", "ra-ap-rustc_lexer", "ra-ap-rustc_parse_format", ] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index 41bcfd11722c4..cd7ec30593656 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -13,8 +13,8 @@ authors.workspace = true [dependencies] ra-ap-rustc_lexer = { version = "0.20.0" } ra-ap-rustc_parse_format = { version = "0.20.0", default-features = false } -ra-ap-rustc_index = { version = "0.19.0", default-features = false } -ra-ap-rustc_abi = { version = "0.19.0", default-features = false } +ra-ap-rustc_index = { version = "0.20.0", default-features = false } +ra-ap-rustc_abi = { version = "0.20.0", default-features = false } [features] in-rust-tree = [] From fc880be491a372ac71de4e6a1f79290fd5e6a967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 25 Nov 2023 20:38:11 +0200 Subject: [PATCH 29/31] Bump chalk --- Cargo.lock | 59 +++++++++++++++++----------------------- crates/hir-ty/Cargo.toml | 8 +++--- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29644efb07c69..b4346972f2a5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,9 +160,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.94.0" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0322d5289ceba3217a03c9af72aa403d87542822b753daa1da32e4b992a4e80" +checksum = "329427f28cd2bddaacd47c4dcd3d7082d315c61fb164394c690fe98c1b6ee9d3" dependencies = [ "proc-macro2", "quote", @@ -172,20 +172,20 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.94.0" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0946cbc6d9136980a24a2dddf1888b5f0aa978dda300a3aa470b55b777b6bf3c" +checksum = "9e1e1659238bd598d0f7dbc5034cf1ff46010a3d6827704c9ed443c8359cb484" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "chalk-derive", "lazy_static", ] [[package]] name = "chalk-recursive" -version = "0.94.0" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd93fedbeeadc0cd4d0eb73bd061b621af99f5324a6a518264c8ef5e526e0ec" +checksum = "b3e0bff0ba1bed11407384fcec0353aeb6888901e63cb47d04505ec47adad847" dependencies = [ "chalk-derive", "chalk-ir", @@ -196,15 +196,15 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.94.0" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a254cff72303c58c82df421cfe9465606372b81588923fcf179922b7eaad9a53" +checksum = "eb9c46d501cf83732a91056c0c846ae7a16d6b3c67a6a6bb5e9cc0a2e91563b6" dependencies = [ "chalk-derive", "chalk-ir", "ena", "indexmap 2.1.0", - "itertools 0.10.5", + "itertools", "petgraph", "rustc-hash", "tracing", @@ -487,7 +487,7 @@ dependencies = [ "hir-def", "hir-expand", "hir-ty", - "itertools 0.12.0", + "itertools", "once_cell", "profile", "rustc-hash", @@ -516,7 +516,7 @@ dependencies = [ "hir-expand", "indexmap 2.1.0", "intern", - "itertools 0.12.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", @@ -544,7 +544,7 @@ dependencies = [ "expect-test", "hashbrown 0.12.3", "intern", - "itertools 0.12.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", @@ -576,7 +576,7 @@ dependencies = [ "hir-def", "hir-expand", "intern", - "itertools 0.12.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "nohash-hasher", @@ -623,7 +623,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools 0.12.0", + "itertools", "nohash-hasher", "oorandom", "profile", @@ -649,7 +649,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.12.0", + "itertools", "profile", "smallvec", "sourcegen", @@ -668,7 +668,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.12.0", + "itertools", "once_cell", "profile", "smallvec", @@ -690,7 +690,7 @@ dependencies = [ "fst", "hir", "indexmap 2.1.0", - "itertools 0.12.0", + "itertools", "limit", "line-index 0.1.0-pre.1", "memchr", @@ -721,7 +721,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.12.0", + "itertools", "once_cell", "profile", "serde_json", @@ -740,7 +740,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.12.0", + "itertools", "nohash-hasher", "parser", "stdx", @@ -819,15 +819,6 @@ dependencies = [ "triomphe", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.0" @@ -941,7 +932,7 @@ dependencies = [ "crossbeam-channel", "ide", "ide-db", - "itertools 0.12.0", + "itertools", "proc-macro-api", "project-model", "tracing", @@ -1393,7 +1384,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", - "itertools 0.12.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "profile", @@ -1578,7 +1569,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", - "itertools 0.12.0", + "itertools", "load-cargo", "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", @@ -1855,7 +1846,7 @@ dependencies = [ "either", "expect-test", "indexmap 2.1.0", - "itertools 0.12.0", + "itertools", "once_cell", "parser", "proc-macro2", @@ -1889,7 +1880,7 @@ dependencies = [ name = "text-edit" version = "0.0.0" dependencies = [ - "itertools 0.12.0", + "itertools", "text-size", ] diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index f2d2451511f7c..bbcb76a43ffef 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -23,10 +23,10 @@ oorandom = "11.1.3" tracing.workspace = true rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.94.0", default-features = false } -chalk-ir = "0.94.0" -chalk-recursive = { version = "0.94.0", default-features = false } -chalk-derive = "0.94.0" +chalk-solve = { version = "0.95.0", default-features = false } +chalk-ir = "0.95.0" +chalk-recursive = { version = "0.95.0", default-features = false } +chalk-derive = "0.95.0" la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true From 3ccde009cbef1cc3153bb0542b86320b34f974a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 26 Nov 2023 13:06:36 +0200 Subject: [PATCH 30/31] Switch to our own salsa fork --- Cargo.lock | 166 ++++++++++++-------------------------- crates/base-db/Cargo.toml | 2 +- 2 files changed, 52 insertions(+), 116 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4346972f2a5e..5a8d971c3d4f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,8 +72,8 @@ dependencies = [ "cfg", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "profile", + "rust-analyzer-salsa", "rustc-hash", - "salsa", "stdx", "syntax", "test-utils", @@ -166,7 +166,7 @@ checksum = "329427f28cd2bddaacd47c4dcd3d7082d315c61fb164394c690fe98c1b6ee9d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", "synstructure", ] @@ -203,7 +203,7 @@ dependencies = [ "chalk-derive", "chalk-ir", "ena", - "indexmap 2.1.0", + "indexmap", "itertools", "petgraph", "rustc-hash", @@ -309,7 +309,7 @@ dependencies = [ "hashbrown 0.12.3", "lock_api", "once_cell", - "parking_lot_core 0.9.6", + "parking_lot_core", ] [[package]] @@ -320,7 +320,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -460,12 +460,9 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -514,7 +511,7 @@ dependencies = [ "fst", "hashbrown 0.12.3", "hir-expand", - "indexmap 2.1.0", + "indexmap", "intern", "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -689,7 +686,7 @@ dependencies = [ "expect-test", "fst", "hir", - "indexmap 2.1.0", + "indexmap", "itertools", "limit", "line-index 0.1.0-pre.1", @@ -760,16 +757,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.1.0" @@ -800,15 +787,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "intern" version = "0.0.0" @@ -1187,17 +1165,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1205,21 +1172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.6", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -1289,7 +1242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.1.0", + "indexmap", ] [[package]] @@ -1477,7 +1430,7 @@ checksum = "3d69f9f6af58124f2da0cb8b0c3d8494e0d883a5fe0c6732258bde81ac5a87cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", "synstructure", ] @@ -1579,8 +1532,8 @@ dependencies = [ "nohash-hasher", "num_cpus", "oorandom", - "parking_lot 0.12.1", - "parking_lot_core 0.9.6", + "parking_lot", + "parking_lot_core", "parser", "proc-macro-api", "profile", @@ -1609,6 +1562,35 @@ dependencies = [ "xshell", ] +[[package]] +name = "rust-analyzer-salsa" +version = "0.17.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca92b657d614d076800aa7bf5d5ba33564e71fa7f16cd79eacdfe301a50ab1c" +dependencies = [ + "crossbeam-utils", + "indexmap", + "lock_api", + "log", + "oorandom", + "parking_lot", + "rust-analyzer-salsa-macros", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "rust-analyzer-salsa-macros" +version = "0.17.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b190359266d293f2ee13eaa502a766dc8b77b63fbaa5d460d24fd0210675ceef" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1637,35 +1619,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" -[[package]] -name = "salsa" -version = "0.17.0-pre.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b223dccb46c32753144d0b51290da7230bb4aedcd8379d6b4c9a474c18bf17a" -dependencies = [ - "crossbeam-utils", - "indexmap 1.9.3", - "lock_api", - "log", - "oorandom", - "parking_lot 0.11.2", - "rustc-hash", - "salsa-macros", - "smallvec", -] - -[[package]] -name = "salsa-macros" -version = "0.17.0-pre.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6c2e352df550bf019da7b16164ed2f7fa107c39653d1311d1bba42d1582ff7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "same-file" version = "1.0.6" @@ -1722,7 +1675,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -1731,7 +1684,7 @@ version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 2.1.0", + "indexmap", "itoa", "ryu", "serde", @@ -1745,7 +1698,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -1804,17 +1757,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.39" @@ -1834,7 +1776,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", "unicode-xid", ] @@ -1845,7 +1787,7 @@ dependencies = [ "cov-mark", "either", "expect-test", - "indexmap 2.1.0", + "indexmap", "itertools", "once_cell", "parser", @@ -1907,7 +1849,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -2008,7 +1950,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn", ] [[package]] @@ -2118,12 +2060,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -2159,7 +2095,7 @@ name = "vfs" version = "0.0.0" dependencies = [ "fst", - "indexmap 2.1.0", + "indexmap", "nohash-hasher", "paths", "rustc-hash", diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml index 171c113a950d0..5ad88f65188e0 100644 --- a/crates/base-db/Cargo.toml +++ b/crates/base-db/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -salsa = "0.17.0-pre.2" +rust-analyzer-salsa = "0.17.0-pre.3" rustc-hash = "1.1.0" triomphe.workspace = true From d036daadda78bc2b64efae66038fcf728c9bd670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 27 Nov 2023 12:54:24 +0200 Subject: [PATCH 31/31] Remove instant from EXCEPTIONS_RUST_ANALYZER --- src/tools/tidy/src/deps.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index f36893bfb94c9..1f8edd7937b49 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -129,7 +129,6 @@ const EXCEPTIONS_CARGO: ExceptionList = &[ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-start ("dissimilar", "Apache-2.0"), - ("instant", "BSD-3-Clause"), ("notify", "CC0-1.0"), ("pulldown-cmark-to-cmark", "Apache-2.0"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0