From 128dc5355b81b0217fede903ae79f75ba0124716 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Fri, 27 Sep 2019 02:04:47 +0800 Subject: [PATCH 1/5] Refactor `Name` ready for hygienic macro --- crates/ra_hir/src/adt.rs | 2 +- crates/ra_hir/src/name.rs | 159 +++++++++++++------------ crates/ra_hir/src/ty/infer.rs | 9 +- crates/ra_syntax/src/ast/extensions.rs | 10 ++ 4 files changed, 95 insertions(+), 85 deletions(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index fbb4ff4d84..99d2862150 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -133,7 +133,7 @@ impl VariantData { .fields() .enumerate() .map(|(i, fd)| StructFieldData { - name: Name::tuple_field_name(i), + name: Name::new_tuple_field(i), type_ref: TypeRef::from_ast_opt(fd.type_ref()), }) .collect(); diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 1bf993ffb0..d50867f5dc 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -5,20 +5,21 @@ use ra_syntax::{ast, SmolStr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are /// not there yet! -#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Name { - text: SmolStr, +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Name(Repr); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +enum Repr { + Text(SmolStr), + TupleField(usize), } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.text, f) - } -} - -impl fmt::Debug for Name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.text, f) + match &self.0 { + Repr::Text(text) => fmt::Display::fmt(&text, f), + Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), + } } } @@ -26,29 +27,38 @@ impl Name { /// Note: this is private to make creating name from random string hard. /// Hopefully, this should allow us to integrate hygiene cleaner in the /// future, and to switch to interned representation of names. - const fn new(text: SmolStr) -> Name { - Name { text } + const fn new_text(text: SmolStr) -> Name { + Name(Repr::Text(text)) + } + + pub(crate) fn new_tuple_field(idx: usize) -> Name { + Name(Repr::TupleField(idx)) + } + + /// Shortcut to create inline plain text name + const fn new_inline_ascii(len: usize, text: &[u8]) -> Name { + Name::new_text(SmolStr::new_inline_from_ascii(len, text)) + } + + /// Resolve a name from the text of token. + fn resolve(raw_text: &SmolStr) -> Name { + let raw_start = "r#"; + if raw_text.as_str().starts_with(raw_start) { + Name::new_text(SmolStr::new(&raw_text[raw_start.len()..])) + } else { + Name::new_text(raw_text.clone()) + } } pub(crate) fn missing() -> Name { - Name::new("[missing name]".into()) + Name::new_text("[missing name]".into()) } - pub(crate) fn tuple_field_name(idx: usize) -> Name { - Name::new(idx.to_string().into()) - } - - // There's should be no way to extract a string out of `Name`: `Name` in the - // future, `Name` will include hygiene information, and you can't encode - // hygiene into a String. - // - // If you need to compare something with `Name`, compare `Name`s directly. - // - // If you need to render `Name` for the user, use the `Display` impl, but be - // aware that it strips hygiene info. - #[deprecated(note = "use to_string instead")] - pub fn as_smolstr(&self) -> &SmolStr { - &self.text + pub(crate) fn as_tuple_index(&self) -> Option { + match self.0 { + Repr::TupleField(idx) => Some(idx), + _ => None, + } } } @@ -58,15 +68,16 @@ pub(crate) trait AsName { impl AsName for ast::NameRef { fn as_name(&self) -> Name { - let name = resolve_name(self.text()); - Name::new(name) + match self.as_tuple_field() { + Some(idx) => Name::new_tuple_field(idx), + None => Name::resolve(self.text()), + } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - let name = resolve_name(self.text()); - Name::new(name) + Name::resolve(self.text()) } } @@ -74,66 +85,56 @@ impl AsName for ast::FieldKind { fn as_name(&self) -> Name { match self { ast::FieldKind::Name(nr) => nr.as_name(), - ast::FieldKind::Index(idx) => Name::new(idx.text().clone()), + ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()), } } } impl AsName for ra_db::Dependency { fn as_name(&self) -> Name { - Name::new(self.name.clone()) + Name::new_text(self.name.clone()) } } // Primitives -pub(crate) const ISIZE: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"isize")); -pub(crate) const I8: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"i8")); -pub(crate) const I16: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i16")); -pub(crate) const I32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i32")); -pub(crate) const I64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i64")); -pub(crate) const I128: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"i128")); -pub(crate) const USIZE: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"usize")); -pub(crate) const U8: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"u8")); -pub(crate) const U16: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u16")); -pub(crate) const U32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u32")); -pub(crate) const U64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u64")); -pub(crate) const U128: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"u128")); -pub(crate) const F32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"f32")); -pub(crate) const F64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"f64")); -pub(crate) const BOOL: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"bool")); -pub(crate) const CHAR: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"char")); -pub(crate) const STR: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"str")); +pub(crate) const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); +pub(crate) const I8: Name = Name::new_inline_ascii(2, b"i8"); +pub(crate) const I16: Name = Name::new_inline_ascii(3, b"i16"); +pub(crate) const I32: Name = Name::new_inline_ascii(3, b"i32"); +pub(crate) const I64: Name = Name::new_inline_ascii(3, b"i64"); +pub(crate) const I128: Name = Name::new_inline_ascii(4, b"i128"); +pub(crate) const USIZE: Name = Name::new_inline_ascii(5, b"usize"); +pub(crate) const U8: Name = Name::new_inline_ascii(2, b"u8"); +pub(crate) const U16: Name = Name::new_inline_ascii(3, b"u16"); +pub(crate) const U32: Name = Name::new_inline_ascii(3, b"u32"); +pub(crate) const U64: Name = Name::new_inline_ascii(3, b"u64"); +pub(crate) const U128: Name = Name::new_inline_ascii(4, b"u128"); +pub(crate) const F32: Name = Name::new_inline_ascii(3, b"f32"); +pub(crate) const F64: Name = Name::new_inline_ascii(3, b"f64"); +pub(crate) const BOOL: Name = Name::new_inline_ascii(4, b"bool"); +pub(crate) const CHAR: Name = Name::new_inline_ascii(4, b"char"); +pub(crate) const STR: Name = Name::new_inline_ascii(3, b"str"); // Special names -pub(crate) const SELF_PARAM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"self")); -pub(crate) const SELF_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Self")); -pub(crate) const MACRO_RULES: Name = Name::new(SmolStr::new_inline_from_ascii(11, b"macro_rules")); +pub(crate) const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); +pub(crate) const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); +pub(crate) const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules"); // Components of known path (value or mod name) -pub(crate) const STD: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"std")); -pub(crate) const ITER: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"iter")); -pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops")); -pub(crate) const FUTURE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future")); -pub(crate) const RESULT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result")); -pub(crate) const BOXED: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"boxed")); +pub(crate) const STD: Name = Name::new_inline_ascii(3, b"std"); +pub(crate) const ITER: Name = Name::new_inline_ascii(4, b"iter"); +pub(crate) const OPS: Name = Name::new_inline_ascii(3, b"ops"); +pub(crate) const FUTURE: Name = Name::new_inline_ascii(6, b"future"); +pub(crate) const RESULT: Name = Name::new_inline_ascii(6, b"result"); +pub(crate) const BOXED: Name = Name::new_inline_ascii(5, b"boxed"); // Components of known path (type name) -pub(crate) const INTO_ITERATOR_TYPE: Name = - Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator")); -pub(crate) const ITEM_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item")); -pub(crate) const TRY_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try")); -pub(crate) const OK_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok")); -pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future")); -pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result")); -pub(crate) const OUTPUT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); -pub(crate) const TARGET_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target")); -pub(crate) const BOX_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Box")); - -fn resolve_name(text: &SmolStr) -> SmolStr { - let raw_start = "r#"; - if text.as_str().starts_with(raw_start) { - SmolStr::new(&text[raw_start.len()..]) - } else { - text.clone() - } -} +pub(crate) const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); +pub(crate) const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); +pub(crate) const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); +pub(crate) const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); +pub(crate) const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); +pub(crate) const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); +pub(crate) const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); +pub(crate) const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); +pub(crate) const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index db33773572..8f92468e67 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -609,7 +609,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for (i, &subpat) in subpats.iter().enumerate() { let expected_ty = def - .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) + .and_then(|d| d.field(self.db, &Name::new_tuple_field(i))) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); let expected_ty = self.normalize_associated_types_in(expected_ty); @@ -1375,10 +1375,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ) .find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) { Ty::Apply(a_ty) => match a_ty.ctor { - TypeCtor::Tuple { .. } => { - let i = name.to_string().parse::().ok(); - i.and_then(|i| a_ty.parameters.0.get(i).cloned()) - } + TypeCtor::Tuple { .. } => name + .as_tuple_index() + .and_then(|idx| a_ty.parameters.0.get(idx).cloned()), TypeCtor::Adt(Adt::Struct(s)) => s.field(self.db, name).map(|field| { self.write_field_resolution(tgt_expr, field); field.ty(self.db).subst(&a_ty.parameters) diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 5f7e9f5b10..0433edb84e 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -21,6 +21,16 @@ impl ast::NameRef { pub fn text(&self) -> &SmolStr { text_of_first_token(self.syntax()) } + + pub fn as_tuple_field(&self) -> Option { + self.syntax().children_with_tokens().find_map(|c| { + if c.kind() == SyntaxKind::INT_NUMBER { + c.as_token().and_then(|tok| tok.text().as_str().parse().ok()) + } else { + None + } + }) + } } fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { From 8cd23a4fb8c6a1012ba3e40dd3329a5abaed06b7 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Fri, 27 Sep 2019 00:16:55 +0800 Subject: [PATCH 2/5] Store crate info in `MacroDefId` --- crates/ra_hir/src/code_model/src.rs | 2 +- crates/ra_hir/src/ids.rs | 9 ++++++--- crates/ra_hir/src/nameres/collector.rs | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs index c0cb27b477..dc964e1566 100644 --- a/crates/ra_hir/src/code_model/src.rs +++ b/crates/ra_hir/src/code_model/src.rs @@ -119,7 +119,7 @@ impl HasSource for TypeAlias { impl HasSource for MacroDef { type Ast = ast::MacroCall; fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source { - Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) } + Source { file_id: self.id.ast_id.file_id(), ast: self.id.ast_id.to_node(db) } } } diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 9ea4e695d9..2463771000 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -10,7 +10,7 @@ use ra_syntax::{ast, AstNode, Parse, SyntaxNode}; use crate::{ db::{AstDatabase, DefDatabase, InternDatabase}, - AstId, FileAstId, Module, Source, + AstId, Crate, FileAstId, Module, Source, }; /// hir makes heavy use of ids: integer (u32) handlers to various things. You @@ -121,10 +121,13 @@ impl From for HirFileId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct MacroDefId(pub(crate) AstId); +pub struct MacroDefId { + pub(crate) ast_id: AstId, + pub(crate) krate: Crate, +} pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option> { - let macro_call = id.0.to_node(db); + let macro_call = id.ast_id.to_node(db); let arg = macro_call.token_tree()?; let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| { log::warn!("fail on macro_def to token tree: {:#?}", arg); diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index ef7dc6ebe3..65929c522b 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -662,7 +662,10 @@ where // Case 1: macro rules, define a macro in crate-global mutable scope if is_macro_rules(&mac.path) { if let Some(name) = &mac.name { - let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); + let macro_id = MacroDefId { + ast_id: mac.ast_id.with_file_id(self.file_id), + krate: self.def_collector.def_map.krate, + }; let macro_ = MacroDef { id: macro_id }; self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); } From 2ecb126f5caeb248e333f8559eb1b7dfd34cc744 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Fri, 27 Sep 2019 01:59:38 +0800 Subject: [PATCH 3/5] Support `$crate` in item and expr place. --- crates/ra_assists/src/assists/auto_import.rs | 2 +- crates/ra_hir/src/expr/lower.rs | 21 ++-- crates/ra_hir/src/generics.rs | 1 + crates/ra_hir/src/ids.rs | 11 ++ crates/ra_hir/src/impl_block.rs | 5 +- crates/ra_hir/src/marks.rs | 2 + crates/ra_hir/src/nameres.rs | 14 +++ crates/ra_hir/src/nameres/raw.rs | 41 ++++--- crates/ra_hir/src/nameres/tests/macros.rs | 105 ++++++++++++++++++ crates/ra_hir/src/path.rs | 64 +++++++++-- crates/ra_hir/src/source_binder.rs | 2 + crates/ra_hir/src/ty/tests.rs | 33 ++++++ crates/ra_hir/src/type_ref.rs | 2 + crates/ra_mbe/src/mbe_expander/transcriber.rs | 2 +- 14 files changed, 268 insertions(+), 37 deletions(-) diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs index 5aae985461..43c14ad238 100644 --- a/crates/ra_assists/src/assists/auto_import.rs +++ b/crates/ra_assists/src/assists/auto_import.rs @@ -512,7 +512,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Option> { hir::PathKind::Plain => {} hir::PathKind::Self_ => ps.push("self".into()), hir::PathKind::Super => ps.push("super".into()), - hir::PathKind::Type(_) => return None, + hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None, } for s in path.segments.iter() { ps.push(s.name.to_string().into()); diff --git a/crates/ra_hir/src/expr/lower.rs b/crates/ra_hir/src/expr/lower.rs index 61535d24fe..6d6f605065 100644 --- a/crates/ra_hir/src/expr/lower.rs +++ b/crates/ra_hir/src/expr/lower.rs @@ -272,8 +272,11 @@ where self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let path = - e.path().and_then(Path::from_ast).map(Expr::Path).unwrap_or(Expr::Missing); + let path = e + .path() + .and_then(|path| self.parse_path(path)) + .map(Expr::Path) + .unwrap_or(Expr::Missing); self.alloc_expr(path, syntax_ptr) } ast::Expr::ContinueExpr(_e) => { @@ -295,7 +298,7 @@ where self.alloc_expr(Expr::Return { expr }, syntax_ptr) } ast::Expr::RecordLit(e) => { - let path = e.path().and_then(Path::from_ast); + let path = e.path().and_then(|path| self.parse_path(path)); let mut field_ptrs = Vec::new(); let record_lit = if let Some(nfl) = e.record_field_list() { let fields = nfl @@ -459,7 +462,7 @@ where .ast_id(&e) .with_file_id(self.current_file_id); - if let Some(path) = e.path().and_then(Path::from_ast) { + if let Some(path) = e.path().and_then(|path| self.parse_path(path)) { if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) { let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db); let file_id = call_id.as_file(MacroFileKind::Expr); @@ -529,7 +532,7 @@ where Pat::Bind { name, mode: annotation, subpat } } ast::Pat::TupleStructPat(p) => { - let path = p.path().and_then(Path::from_ast); + let path = p.path().and_then(|path| self.parse_path(path)); let args = p.args().map(|p| self.collect_pat(p)).collect(); Pat::TupleStruct { path, args } } @@ -539,7 +542,7 @@ where Pat::Ref { pat, mutability } } ast::Pat::PathPat(p) => { - let path = p.path().and_then(Path::from_ast); + let path = p.path().and_then(|path| self.parse_path(path)); path.map(Pat::Path).unwrap_or(Pat::Missing) } ast::Pat::TuplePat(p) => { @@ -548,7 +551,7 @@ where } ast::Pat::PlaceholderPat(_) => Pat::Wild, ast::Pat::RecordPat(p) => { - let path = p.path().and_then(Path::from_ast); + let path = p.path().and_then(|path| self.parse_path(path)); let record_field_pat_list = p.record_field_pat_list().expect("every struct should have a field list"); let mut fields: Vec<_> = record_field_pat_list @@ -589,6 +592,10 @@ where self.missing_pat() } } + + fn parse_path(&mut self, path: ast::Path) -> Option { + Path::from_src(Source { ast: path, file_id: self.current_file_id }, self.db) + } } impl From for BinaryOp { diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 6865d34ba7..4ce7551c33 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -132,6 +132,7 @@ impl GenericParams { fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { for (idx, type_param) in params.type_params().enumerate() { let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); + // FIXME: Use `Path::from_src` let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast); let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default }; diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 2463771000..bcbcd3dd77 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -58,6 +58,17 @@ impl HirFileId { } } + /// Get the crate which the macro lives in, if it is a macro file. + pub(crate) fn macro_crate(self, db: &impl AstDatabase) -> Option { + match self.0 { + HirFileIdRepr::File(_) => None, + HirFileIdRepr::Macro(macro_file) => { + let loc = macro_file.macro_call_id.loc(db); + Some(loc.def.krate) + } + } + } + pub(crate) fn parse_or_expand_query( db: &impl AstDatabase, file_id: HirFileId, diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index d830202bd2..c66a1c6a6d 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -218,7 +218,10 @@ impl ModuleImplBlocks { ast::ItemOrMacro::Macro(macro_call) => { //FIXME: we should really cut down on the boilerplate required to process a macro let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); - if let Some(path) = macro_call.path().and_then(Path::from_ast) { + if let Some(path) = macro_call + .path() + .and_then(|path| Path::from_src(Source { ast: path, file_id }, db)) + { if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path) { let call_id = MacroCallLoc { def: def.id, ast_id }.id(db); diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index b2111be053..72f76bb797 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -14,4 +14,6 @@ test_utils::marks!( macro_rules_from_other_crates_are_visible_with_macro_use prelude_is_macro_use coerce_merge_fail_fallback + macro_dollar_crate_self + macro_dollar_crate_other ); diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index b808a0c36c..15b5b4ee67 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -332,6 +332,20 @@ impl CrateDefMap { ) -> ResolvePathResult { let mut segments = path.segments.iter().enumerate(); let mut curr_per_ns: PerNs = match path.kind { + PathKind::DollarCrate(krate) => { + if krate == self.krate { + tested_by!(macro_dollar_crate_self); + PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) + } else { + match krate.root_module(db) { + Some(module) => { + tested_by!(macro_dollar_crate_other); + PerNs::types(module.into()) + } + None => return ResolvePathResult::empty(ReachedFixedPoint::No), + } + } + } PathKind::Crate => { PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 29aaddbf1b..c607b8a119 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -9,7 +9,7 @@ use test_utils::tested_by; use crate::{ db::{AstDatabase, DefDatabase}, - AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, + AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, }; /// `RawItems` is a set of top-level items in a file (except for impls). @@ -71,6 +71,8 @@ impl RawItems { raw_items: RawItems::default(), source_ast_id_map: db.ast_id_map(file_id), source_map: ImportSourceMap::default(), + file_id, + db, }; if let Some(node) = db.parse_or_expand(file_id) { if let Some(source_file) = ast::SourceFile::cast(node.clone()) { @@ -192,13 +194,15 @@ pub(super) struct MacroData { pub(super) export: bool, } -struct RawItemsCollector { +struct RawItemsCollector { raw_items: RawItems, source_ast_id_map: Arc, source_map: ImportSourceMap, + file_id: HirFileId, + db: DB, } -impl RawItemsCollector { +impl RawItemsCollector<&'_ DB> { fn process_module(&mut self, current_module: Option, body: impl ast::ModuleItemOwner) { for item_or_macro in body.items_with_macros() { match item_or_macro { @@ -300,17 +304,21 @@ impl RawItemsCollector { fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { let is_prelude = use_item.has_atom_attr("prelude_import"); - Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| { - let import_data = ImportData { - path, - alias, - is_glob, - is_prelude, - is_extern_crate: false, - is_macro_use: false, - }; - self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); - }) + Path::expand_use_item( + Source { ast: use_item, file_id: self.file_id }, + self.db, + |path, use_tree, is_glob, alias| { + let import_data = ImportData { + path, + alias, + is_glob, + is_prelude, + is_extern_crate: false, + is_macro_use: false, + }; + self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); + }, + ) } fn add_extern_crate_item( @@ -335,7 +343,10 @@ impl RawItemsCollector { } fn add_macro(&mut self, current_module: Option, m: ast::MacroCall) { - let path = match m.path().and_then(Path::from_ast) { + let path = match m + .path() + .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) + { Some(it) => it, _ => return, }; diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index bd60f4258d..e4b4083948 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs @@ -515,3 +515,108 @@ fn path_qualified_macros() { ⋮not_found: _ "###); } + +#[test] +fn macro_dollar_crate_is_correct_in_item() { + covers!(macro_dollar_crate_self); + covers!(macro_dollar_crate_other); + let map = def_map_with_crate_graph( + " + //- /main.rs + #[macro_use] + extern crate foo; + + #[macro_use] + mod m { + macro_rules! current { + () => { + use $crate::Foo as FooSelf; + } + } + } + + struct Foo; + + current!(); + not_current1!(); + foo::not_current2!(); + + //- /lib.rs + mod m { + #[macro_export] + macro_rules! not_current1 { + () => { + use $crate::Bar; + } + } + } + + #[macro_export] + macro_rules! not_current2 { + () => { + use $crate::Baz; + } + } + + struct Bar; + struct Baz; + ", + crate_graph! { + "main": ("/main.rs", ["foo"]), + "foo": ("/lib.rs", []), + }, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + ⋮Foo: t v + ⋮FooSelf: t v + ⋮foo: t + ⋮m: t + ⋮ + ⋮crate::m + "###); +} + +#[test] +fn macro_dollar_crate_is_correct_in_indirect_deps() { + covers!(macro_dollar_crate_other); + // From std + let map = def_map_with_crate_graph( + r#" + //- /main.rs + foo!(); + + //- /std.rs + #[prelude_import] + use self::prelude::*; + + pub use core::foo; + + mod prelude {} + + #[macro_use] + mod std_macros; + + //- /core.rs + #[macro_export] + macro_rules! foo { + () => { + use $crate::bar; + } + } + + pub struct bar; + "#, + crate_graph! { + "main": ("/main.rs", ["std"]), + "std": ("/std.rs", ["core"]), + "core": ("/core.rs", []), + }, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t v + "###); +} diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 39d1b7e46f..158c853d42 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -5,7 +5,7 @@ use ra_syntax::{ AstNode, }; -use crate::{name, type_ref::TypeRef, AsName, Name}; +use crate::{db::AstDatabase, name, type_ref::TypeRef, AsName, Crate, Name, Source}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { @@ -52,16 +52,19 @@ pub enum PathKind { Abs, // Type based path like `::foo` Type(Box), + // `$crate` from macro expansion + DollarCrate(Crate), } impl Path { /// Calls `cb` with all paths, represented by this use item. pub fn expand_use_item( - item: &ast::UseItem, + item_src: Source, + db: &impl AstDatabase, mut cb: impl FnMut(Path, &ast::UseTree, bool, Option), ) { - if let Some(tree) = item.use_tree() { - expand_use_tree(None, tree, &mut cb); + if let Some(tree) = item_src.ast.use_tree() { + expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb); } } @@ -76,7 +79,19 @@ impl Path { } /// Converts an `ast::Path` to `Path`. Works with use trees. - pub fn from_ast(mut path: ast::Path) -> Option { + /// DEPRECATED: It does not handle `$crate` from macro call. + pub fn from_ast(path: ast::Path) -> Option { + Path::parse(path, &|| None) + } + + /// Converts an `ast::Path` to `Path`. Works with use trees. + /// It correctly handles `$crate` based path from macro call. + pub fn from_src(source: Source, db: &impl AstDatabase) -> Option { + let file_id = source.file_id; + Path::parse(source.ast, &|| file_id.macro_crate(db)) + } + + fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option) -> Option { let mut kind = PathKind::Plain; let mut segments = Vec::new(); loop { @@ -88,6 +103,13 @@ impl Path { match segment.kind()? { ast::PathSegmentKind::Name(name) => { + if name.text() == "$crate" { + if let Some(macro_crate) = macro_crate() { + kind = PathKind::DollarCrate(macro_crate); + break; + } + } + let args = segment .type_arg_list() .and_then(GenericArgs::from_ast) @@ -113,7 +135,7 @@ impl Path { } // >::Foo desugars to Trait::Foo Some(trait_ref) => { - let path = Path::from_ast(trait_ref.path()?)?; + let path = Path::parse(trait_ref.path()?, macro_crate)?; kind = path.kind; let mut prefix_segments = path.segments; prefix_segments.reverse(); @@ -264,6 +286,7 @@ impl From for Path { fn expand_use_tree( prefix: Option, tree: ast::UseTree, + macro_crate: &impl Fn() -> Option, cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option), ) { if let Some(use_tree_list) = tree.use_tree_list() { @@ -272,13 +295,13 @@ fn expand_use_tree( None => prefix, // E.g. `use something::{inner}` (prefix is `None`, path is `something`) // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) - Some(path) => match convert_path(prefix, path) { + Some(path) => match convert_path(prefix, path, macro_crate) { Some(it) => Some(it), None => return, // FIXME: report errors somewhere }, }; for child_tree in use_tree_list.use_trees() { - expand_use_tree(prefix.clone(), child_tree, cb); + expand_use_tree(prefix.clone(), child_tree, macro_crate, cb); } } else { let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name()); @@ -295,7 +318,7 @@ fn expand_use_tree( } } } - if let Some(path) = convert_path(prefix, ast_path) { + if let Some(path) = convert_path(prefix, ast_path, macro_crate) { let is_glob = tree.has_star(); cb(path, &tree, is_glob, alias) } @@ -305,12 +328,29 @@ fn expand_use_tree( } } -fn convert_path(prefix: Option, path: ast::Path) -> Option { - let prefix = - if let Some(qual) = path.qualifier() { Some(convert_path(prefix, qual)?) } else { prefix }; +fn convert_path( + prefix: Option, + path: ast::Path, + macro_crate: &impl Fn() -> Option, +) -> Option { + let prefix = if let Some(qual) = path.qualifier() { + Some(convert_path(prefix, qual, macro_crate)?) + } else { + prefix + }; + let segment = path.segment()?; let res = match segment.kind()? { ast::PathSegmentKind::Name(name) => { + if name.text() == "$crate" { + if let Some(krate) = macro_crate() { + return Some(Path::from_simple_segments( + PathKind::DollarCrate(krate), + iter::empty(), + )); + } + } + // no type args in use let mut res = prefix .unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) }); diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index bd4be84301..6e89bfc765 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -203,6 +203,7 @@ impl SourceAnalyzer { db: &impl HirDatabase, macro_call: &ast::MacroCall, ) -> Option { + // This must be a normal source file rather than macro file. let path = macro_call.path().and_then(Path::from_ast)?; self.resolver.resolve_path_as_macro(db, &path) } @@ -261,6 +262,7 @@ impl SourceAnalyzer { return Some(PathResolution::AssocItem(assoc)); } } + // This must be a normal source file rather than macro file. let hir_path = crate::Path::from_ast(path.clone())?; self.resolve_hir_path(db, &hir_path) } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 4362bb27a3..bd2b07755c 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3130,6 +3130,39 @@ fn test() { S.foo()<|>; } assert_eq!(t, "u128"); } +#[test] +fn infer_macro_with_dollar_crate_is_correct_in_expr() { + covers!(macro_dollar_crate_other); + let (mut db, pos) = MockDatabase::with_position( + r#" +//- /main.rs +fn test() { + let x = (foo::foo!(1), foo::foo!(2)); + x<|>; +} + +//- /lib.rs +#[macro_export] +macro_rules! foo { + (1) => { $crate::bar!() }; + (2) => { 1 + $crate::baz() }; +} + +#[macro_export] +macro_rules! bar { + () => { 42 } +} + +pub fn baz() -> usize { 31usize } +"#, + ); + db.set_crate_graph_from_fixture(crate_graph! { + "main": ("/main.rs", ["foo"]), + "foo": ("/lib.rs", []), + }); + assert_eq!("(i32, usize)", type_at_pos(&db, pos)); +} + #[ignore] #[test] fn method_resolution_trait_before_autoref() { diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index bc8acc7eed..2cf06b2504 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -72,6 +72,7 @@ impl TypeRef { } ast::TypeRef::NeverType(..) => TypeRef::Never, ast::TypeRef::PathType(inner) => { + // FIXME: Use `Path::from_src` inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error) } ast::TypeRef::PointerType(inner) => { @@ -141,6 +142,7 @@ impl TypeBound { Some(p) => p, None => return TypeBound::Error, }; + // FIXME: Use `Path::from_src` let path = match Path::from_ast(path) { Some(p) => p, None => return TypeBound::Error, diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index c22680b93a..ed094d5bb4 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs @@ -86,7 +86,7 @@ fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result Result { let res = if v == "crate" { - // FIXME: Properly handle $crate token + // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) .into(); From edadeb95be16a69175e94a0e211ae9bb74267abb Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Fri, 27 Sep 2019 10:55:25 +0800 Subject: [PATCH 4/5] Fix --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_hir/src/nameres/raw.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 15b5b4ee67..67adcfa288 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -342,7 +342,7 @@ impl CrateDefMap { tested_by!(macro_dollar_crate_other); PerNs::types(module.into()) } - None => return ResolvePathResult::empty(ReachedFixedPoint::No), + None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), } } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index c607b8a119..c494b95b0e 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -202,7 +202,7 @@ struct RawItemsCollector { db: DB, } -impl RawItemsCollector<&'_ DB> { +impl RawItemsCollector<&DB> { fn process_module(&mut self, current_module: Option, body: impl ast::ModuleItemOwner) { for item_or_macro in body.items_with_macros() { match item_or_macro { From 35f1655b0bc0452ee5f2bed521ebb12c17a8a20d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 27 Sep 2019 20:47:36 +0300 Subject: [PATCH 5/5] replace horrible hack with a slightly less horrible one --- crates/ra_db/src/lib.rs | 5 ++--- crates/ra_hir/src/ty/traits.rs | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index c54791b7ae..603daed374 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -32,11 +32,10 @@ pub trait CheckCanceled { fn catch_canceled(&self, f: F) -> Result where - Self: Sized, + Self: Sized + panic::RefUnwindSafe, F: FnOnce(&Self) -> T + panic::UnwindSafe, { - let this = panic::AssertUnwindSafe(self); - panic::catch_unwind(|| f(*this)).map_err(|err| match err.downcast::() { + panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::() { Ok(canceled) => *canceled, Err(payload) => panic::resume_unwind(payload), }) diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index d11dab2940..90a11ac7d5 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -30,6 +30,9 @@ impl PartialEq for TraitSolver { impl Eq for TraitSolver {} +// FIXME: this impl is WRONG, chalk is not RefUnwindSafe, and this causes #1927 +impl std::panic::RefUnwindSafe for TraitSolver {} + impl TraitSolver { fn solve( &self,