mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	fix: Handle included files better in IDE layer
This does not fully fix things, but it introduces a function that can be used to fix occurences. When using `to_def` functionality, the input node needs to come from the macro expanded include, not the real file that was included. This does unfortunately add more caller burden, but there is not really a way around it.
This commit is contained in:
		
							parent
							
								
									6440fe2a01
								
							
						
					
					
						commit
						e72738de99
					
				
					 3 changed files with 97 additions and 31 deletions
				
			
		|  | @ -769,6 +769,31 @@ impl<'db> SemanticsImpl<'db> { | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Descends the token into the include expansion, if its file is an included file.
 | ||||||
|  |     pub fn descend_token_into_include_expansion( | ||||||
|  |         &self, | ||||||
|  |         tok: InRealFile<SyntaxToken>, | ||||||
|  |     ) -> InFile<SyntaxToken> { | ||||||
|  |         let Some(include) = | ||||||
|  |             self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, tok.file_id) | ||||||
|  |         else { | ||||||
|  |             return tok.into(); | ||||||
|  |         }; | ||||||
|  |         let span = self.db.real_span_map(tok.file_id).span_for_range(tok.value.text_range()); | ||||||
|  |         let Some(InMacroFile { file_id, value: mut mapped_tokens }) = self.with_ctx(|ctx| { | ||||||
|  |             Some( | ||||||
|  |                 ctx.cache | ||||||
|  |                     .get_or_insert_expansion(ctx.db, include) | ||||||
|  |                     .map_range_down(span)? | ||||||
|  |                     .map(SmallVec::<[_; 2]>::from_iter), | ||||||
|  |             ) | ||||||
|  |         }) else { | ||||||
|  |             return tok.into(); | ||||||
|  |         }; | ||||||
|  |         // We should only get one result at most
 | ||||||
|  |         mapped_tokens.pop().map_or_else(|| tok.into(), |(tok, _)| InFile::new(file_id.into(), tok)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Maps a node down by mapping its first and last token down.
 |     /// Maps a node down by mapping its first and last token down.
 | ||||||
|     pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> { |     pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> { | ||||||
|         // This might not be the correct way to do this, but it works for now
 |         // This might not be the correct way to do this, but it works for now
 | ||||||
|  | @ -1528,11 +1553,9 @@ impl<'db> SemanticsImpl<'db> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn resolve_macro_call2(&self, macro_call: InFile<&ast::MacroCall>) -> Option<Macro> { |     pub fn resolve_macro_call2(&self, macro_call: InFile<&ast::MacroCall>) -> Option<Macro> { | ||||||
|         self.with_ctx(|ctx| { |         self.to_def2(macro_call) | ||||||
|             ctx.macro_call_to_macro_call(macro_call) |             .and_then(|call| self.with_ctx(|ctx| macro_call_to_macro_id(ctx, call))) | ||||||
|                 .and_then(|call| macro_call_to_macro_id(ctx, call)) |  | ||||||
|             .map(Into::into) |             .map(Into::into) | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn is_proc_macro_call(&self, macro_call: InFile<&ast::MacroCall>) -> bool { |     pub fn is_proc_macro_call(&self, macro_call: InFile<&ast::MacroCall>) -> bool { | ||||||
|  | @ -1647,6 +1670,10 @@ impl<'db> SemanticsImpl<'db> { | ||||||
|         T::to_def(self, src) |         T::to_def(self, src) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn to_def2<T: ToDef>(&self, src: InFile<&T>) -> Option<T::Def> { | ||||||
|  |         T::to_def(self, src) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn file_to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> { |     fn file_to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> { | ||||||
|         self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from) |         self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -399,19 +399,6 @@ impl SourceToDefCtx<'_, '_> { | ||||||
|         Some((container, label?)) |         Some((container, label?)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> { |  | ||||||
|         let map = self.dyn_map(src)?; |  | ||||||
|         map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub(super) fn macro_call_to_macro_call( |  | ||||||
|         &mut self, |  | ||||||
|         src: InFile<&ast::MacroCall>, |  | ||||||
|     ) -> Option<MacroCallId> { |  | ||||||
|         let map = self.dyn_map(src)?; |  | ||||||
|         map[keys::MACRO_CALL].get(&AstPtr::new(src.value)).copied() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// (AttrId, derive attribute call id, derive call ids)
 |     /// (AttrId, derive attribute call id, derive call ids)
 | ||||||
|     pub(super) fn attr_to_derive_macro_call( |     pub(super) fn attr_to_derive_macro_call( | ||||||
|         &mut self, |         &mut self, | ||||||
|  | @ -449,6 +436,17 @@ impl SourceToDefCtx<'_, '_> { | ||||||
|             .or_insert_with(|| container.child_by_source(db, file_id)) |             .or_insert_with(|| container.child_by_source(db, file_id)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> { | ||||||
|  |         self.to_def(src, keys::ATTR_MACRO_CALL) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(super) fn macro_call_to_macro_call( | ||||||
|  |         &mut self, | ||||||
|  |         src: InFile<&ast::MacroCall>, | ||||||
|  |     ) -> Option<MacroCallId> { | ||||||
|  |         self.to_def(src, keys::MACRO_CALL) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub(super) fn type_param_to_def( |     pub(super) fn type_param_to_def( | ||||||
|         &mut self, |         &mut self, | ||||||
|         src: InFile<&ast::TypeParam>, |         src: InFile<&ast::TypeParam>, | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| use hir::db::ExpandDatabase; | use hir::db::ExpandDatabase; | ||||||
| use hir::{ExpandResult, InFile, Semantics}; | use hir::{ExpandResult, InFile, InRealFile, Semantics}; | ||||||
| use ide_db::{ | use ide_db::{ | ||||||
|     FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, |     FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, | ||||||
|     syntax_helpers::prettify_macro_expansion, |     syntax_helpers::prettify_macro_expansion, | ||||||
| }; | }; | ||||||
| use span::{Edition, SpanMap, SyntaxContext, TextRange, TextSize}; | use span::{SpanMap, SyntaxContext, TextRange, TextSize}; | ||||||
| use stdx::format_to; | use stdx::format_to; | ||||||
| use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted}; | use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted}; | ||||||
| 
 | 
 | ||||||
|  | @ -26,8 +26,9 @@ pub struct ExpandedMacro { | ||||||
| // 
 | // 
 | ||||||
| pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | ||||||
|     let sema = Semantics::new(db); |     let sema = Semantics::new(db); | ||||||
|     let file = sema.parse_guess_edition(position.file_id); |     let file_id = sema.attach_first_edition(position.file_id)?; | ||||||
|     let krate = sema.file_to_module_def(position.file_id)?.krate().into(); |     let file = sema.parse(file_id); | ||||||
|  |     let krate = sema.file_to_module_def(file_id.file_id(db))?.krate().into(); | ||||||
| 
 | 
 | ||||||
|     let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { |     let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { | ||||||
|         SyntaxKind::IDENT => 1, |         SyntaxKind::IDENT => 1, | ||||||
|  | @ -86,7 +87,10 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | ||||||
|         return derive; |         return derive; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let mut anc = tok.parent_ancestors(); |     let mut anc = sema | ||||||
|  |         .descend_token_into_include_expansion(InRealFile::new(file_id, tok)) | ||||||
|  |         .value | ||||||
|  |         .parent_ancestors(); | ||||||
|     let mut span_map = SpanMap::empty(); |     let mut span_map = SpanMap::empty(); | ||||||
|     let mut error = String::new(); |     let mut error = String::new(); | ||||||
|     let (name, expanded, kind) = loop { |     let (name, expanded, kind) = loop { | ||||||
|  | @ -95,14 +99,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | ||||||
|         if let Some(item) = ast::Item::cast(node.clone()) { |         if let Some(item) = ast::Item::cast(node.clone()) { | ||||||
|             if let Some(def) = sema.resolve_attr_macro_call(&item) { |             if let Some(def) = sema.resolve_attr_macro_call(&item) { | ||||||
|                 break ( |                 break ( | ||||||
|                     def.name(db) |                     def.name(db).display(db, file_id.edition(db)).to_string(), | ||||||
|                         .display( |  | ||||||
|                             db, |  | ||||||
|                             sema.attach_first_edition(position.file_id) |  | ||||||
|                                 .map(|it| it.edition(db)) |  | ||||||
|                                 .unwrap_or(Edition::CURRENT), |  | ||||||
|                         ) |  | ||||||
|                         .to_string(), |  | ||||||
|                     expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, |                     expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, | ||||||
|                     SyntaxKind::MACRO_ITEMS, |                     SyntaxKind::MACRO_ITEMS, | ||||||
|                 ); |                 ); | ||||||
|  | @ -759,4 +756,48 @@ fn test() { | ||||||
|                 "<>hi""#]],
 |                 "<>hi""#]],
 | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn in_included() { | ||||||
|  |         check( | ||||||
|  |             r#" | ||||||
|  | //- minicore: include
 | ||||||
|  | //- /main.rs crate:main
 | ||||||
|  | include!("./included.rs"); | ||||||
|  | //- /included.rs
 | ||||||
|  | macro_rules! foo { | ||||||
|  |     () => { fn item() {} }; | ||||||
|  | } | ||||||
|  | foo$0!(); | ||||||
|  | "#,
 | ||||||
|  |             expect![[r#" | ||||||
|  |                 foo! | ||||||
|  |                 fn item(){}"#]],
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn include() { | ||||||
|  |         check( | ||||||
|  |             r#" | ||||||
|  | //- minicore: include
 | ||||||
|  | //- /main.rs crate:main
 | ||||||
|  | include$0!("./included.rs"); | ||||||
|  | //- /included.rs
 | ||||||
|  | macro_rules! foo { | ||||||
|  |     () => { fn item() {} }; | ||||||
|  | } | ||||||
|  | foo(); | ||||||
|  | "#,
 | ||||||
|  |             expect![[r#" | ||||||
|  |                 include! | ||||||
|  |                 macro_rules! foo { | ||||||
|  |                     () => { | ||||||
|  |                         fn item(){} | ||||||
|  | 
 | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |                 foo();"#]],
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth