diff --git a/Cargo.toml b/Cargo.toml index 3cecae0dc4..1029844cd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.82" +rust-version = "1.83" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 54a61a096d..cc53d2e34a 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -11,7 +11,7 @@ use syntax::utils::is_raw_identifier; /// and declarations. In theory, names should also carry hygiene info, but we are /// not there yet! /// -/// Note that the rawness (`r#`) of names does not depend on whether they are written raw. +/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix. /// This is because we want to show (in completions etc.) names as raw depending on the needs /// of the current crate, for example if it is edition 2021 complete `gen` even if the defining /// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well. @@ -77,6 +77,7 @@ impl Name { /// Hopefully, this should allow us to integrate hygiene cleaner in the /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { + debug_assert!(!text.starts_with("r#")); Name { symbol: Symbol::intern(text), ctx: () } } @@ -91,15 +92,34 @@ impl Name { pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. - Self::new(text, SyntaxContextId::root(Edition::Edition2015)) + Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015)) } pub fn new_tuple_field(idx: usize) -> Name { - Name { symbol: Symbol::intern(&idx.to_string()), ctx: () } + let symbol = match idx { + 0 => sym::INTEGER_0.clone(), + 1 => sym::INTEGER_1.clone(), + 2 => sym::INTEGER_2.clone(), + 3 => sym::INTEGER_3.clone(), + 4 => sym::INTEGER_4.clone(), + 5 => sym::INTEGER_5.clone(), + 6 => sym::INTEGER_6.clone(), + 7 => sym::INTEGER_7.clone(), + 8 => sym::INTEGER_8.clone(), + 9 => sym::INTEGER_9.clone(), + 10 => sym::INTEGER_10.clone(), + 11 => sym::INTEGER_11.clone(), + 12 => sym::INTEGER_12.clone(), + 13 => sym::INTEGER_13.clone(), + 14 => sym::INTEGER_14.clone(), + 15 => sym::INTEGER_15.clone(), + _ => Symbol::intern(&idx.to_string()), + }; + Name { symbol, ctx: () } } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () } + Self::new_text(lt.text().as_str().trim_start_matches("r#")) } /// Resolve a name from the text of token. @@ -142,15 +162,18 @@ impl Name { } /// Returns the text this name represents if it isn't a tuple field. + /// + /// Do not use this for user-facing text, use `display` instead to handle editions properly. pub fn as_str(&self) -> &str { self.symbol.as_str() } + // FIXME: Remove this pub fn unescaped(&self) -> UnescapedName<'_> { UnescapedName(self) } - pub fn is_escaped(&self, edition: Edition) -> bool { + pub fn needs_escape(&self, edition: Edition) -> bool { is_raw_identifier(self.symbol.as_str(), edition) } @@ -173,16 +196,19 @@ impl Name { &self.symbol } - pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); _ = ctx; Self { symbol, ctx: () } } // FIXME: This needs to go once we have hygiene - pub const fn new_symbol_root(sym: Symbol) -> Self { + pub fn new_symbol_root(sym: Symbol) -> Self { + debug_assert!(!sym.as_str().starts_with("r#")); Self { symbol: sym, ctx: () } } + // FIXME: Remove this #[inline] pub fn eq_ident(&self, ident: &str) -> bool { self.as_str() == ident.trim_start_matches("r#") diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 92f6c4b5ab..af98e5f2fd 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -39,8 +39,8 @@ use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, - AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, - TextRange, TextSize, + AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, + TextSize, }; use triomphe::Arc; @@ -1587,14 +1587,11 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_mod_path_relative( &self, to: Module, - segments: impl IntoIterator, + segments: impl IntoIterator, ) -> Option> { let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items( self.db.upcast(), - &ModPath::from_segments( - hir_def::path::PathKind::Plain, - segments.into_iter().map(|it| Name::new_root(&it)), - ), + &ModPath::from_segments(hir_def::path::PathKind::Plain, segments), ); Some(items.iter_items().map(|(item, _)| item.into())) } diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 3b2b2fd706..73313eeaa6 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T}; +use syntax::{ast, AstNode, SyntaxNode, ToSmolStr}; use crate::{ config::AutoImportExclusionType, @@ -403,10 +403,11 @@ fn import_on_the_fly_method( fn import_name(ctx: &CompletionContext<'_>) -> String { let token_kind = ctx.token.kind(); - if matches!(token_kind, T![.] | T![::]) { - String::new() - } else { + + if token_kind.is_any_identifier() { ctx.token.to_string() + } else { + String::new() } } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 7fee224090..61e8114d38 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -423,7 +423,7 @@ fn render_resolution_path( let name = local_name.display_no_db(ctx.completion.edition).to_smolstr(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.is_escaped(completion.edition) { + if local_name.needs_escape(completion.edition) { item.insert_text(local_name.display_no_db(completion.edition).to_smolstr()); } // Add `<>` for generic types diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 573018f9cc..ad86d855b5 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -4,14 +4,14 @@ use std::ops::ControlFlow; use hir::{ db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, - ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, + ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Trait, TyFingerprint, Type, }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ ast::{self, make, HasName}, - AstNode, SmolStr, SyntaxNode, + AstNode, SyntaxNode, }; use crate::{ @@ -53,7 +53,7 @@ pub struct TraitImportCandidate { #[derive(Debug)] pub struct PathImportCandidate { /// Optional qualifier before name. - pub qualifier: Vec, + pub qualifier: Vec, /// The name the item (struct, trait, enum, etc.) should have. pub name: NameToImport, } @@ -72,10 +72,18 @@ pub enum NameToImport { impl NameToImport { pub fn exact_case_sensitive(s: String) -> NameToImport { + let s = match s.strip_prefix("r#") { + Some(s) => s.to_owned(), + None => s, + }; NameToImport::Exact(s, true) } pub fn fuzzy(s: String) -> NameToImport { + let s = match s.strip_prefix("r#") { + Some(s) => s.to_owned(), + None => s, + }; // unless all chars are lowercase, we do a case sensitive search let case_sensitive = s.chars().any(|c| c.is_uppercase()); NameToImport::Fuzzy(s, case_sensitive) @@ -359,7 +367,7 @@ fn path_applicable_imports( [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name( sema, current_crate, - NameToImport::Exact(first_qsegment.to_string(), true), + NameToImport::Exact(first_qsegment.as_str().to_owned(), true), AssocSearchMode::Exclude, ) .filter_map(|item| { @@ -389,7 +397,7 @@ fn validate_resolvable( scope_filter: impl Fn(ItemInNs) -> bool, candidate: &NameToImport, resolved_qualifier: ItemInNs, - unresolved_qualifier: &[SmolStr], + unresolved_qualifier: &[Name], ) -> Option { let _p = tracing::info_span!("ImportAssets::import_for_item").entered(); @@ -722,7 +730,7 @@ fn path_import_candidate( if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() - .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) + .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) .collect::>>()?; ImportCandidate::Path(PathImportCandidate { qualifier, name }) } else { diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 405d2d91d8..a2062f36d3 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -141,10 +141,11 @@ fn find_items<'a>( // Query the local crate using the symbol index. let mut local_results = Vec::new(); local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| { - ControlFlow::<()>::Continue(local_results.push(match local_candidate.def { + local_results.push(match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), - })) + }); + ControlFlow::<()>::Continue(()) }); local_results.into_iter().chain(external_importables) } diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index 738db95133..c94644eeb8 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -220,7 +220,10 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { }; let mut res = vec![]; - query.search::<()>(&indices, |f| ControlFlow::Continue(res.push(f.clone()))); + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); res }