mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	Fix import search not discarding rawness
This commit is contained in:
		
							parent
							
								
									20ff27e2ba
								
							
						
					
					
						commit
						a06606c802
					
				
					 8 changed files with 65 additions and 29 deletions
				
			
		|  | @ -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"] | ||||
|  |  | |||
|  | @ -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#") | ||||
|  |  | |||
|  | @ -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<Item = SmolStr>, | ||||
|         segments: impl IntoIterator<Item = Name>, | ||||
|     ) -> Option<impl Iterator<Item = ItemInNs>> { | ||||
|         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())) | ||||
|     } | ||||
|  |  | |||
|  | @ -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() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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<SmolStr>, | ||||
|     pub qualifier: Vec<Name>, | ||||
|     /// 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<LocatedImport> { | ||||
|     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::<Option<Vec<_>>>()?; | ||||
|                     ImportCandidate::Path(PathImportCandidate { qualifier, name }) | ||||
|                 } else { | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -220,7 +220,10 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { | |||
|     }; | ||||
| 
 | ||||
|     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 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth