From 2619950b3b405324ab1c1745876165c834b3b4b9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 13 Dec 2019 12:12:36 +0100 Subject: Use different types for path with and without generics --- crates/ra_hir_def/src/attr.rs | 6 +- crates/ra_hir_def/src/body.rs | 2 +- crates/ra_hir_def/src/nameres.rs | 4 +- crates/ra_hir_def/src/nameres/collector.rs | 14 +- crates/ra_hir_def/src/nameres/path_resolution.rs | 24 +- crates/ra_hir_def/src/nameres/raw.rs | 12 +- crates/ra_hir_def/src/path.rs | 345 ++++++++++------------- crates/ra_hir_def/src/path/lower.rs | 176 ++++++++++++ crates/ra_hir_def/src/path/lower/lower_use.rs | 112 ++++++++ crates/ra_hir_def/src/path/lower_use.rs | 115 -------- crates/ra_hir_def/src/resolver.rs | 46 ++- 11 files changed, 487 insertions(+), 369 deletions(-) create mode 100644 crates/ra_hir_def/src/path/lower.rs create mode 100644 crates/ra_hir_def/src/path/lower/lower_use.rs delete mode 100644 crates/ra_hir_def/src/path/lower_use.rs (limited to 'crates/ra_hir_def/src') diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 5bf82e191..9efa4970c 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs @@ -12,7 +12,7 @@ use ra_syntax::{ use tt::Subtree; use crate::{ - db::DefDatabase, path::Path, src::HasChildSource, src::HasSource, AdtId, AttrDefId, Lookup, + db::DefDatabase, path::ModPath, src::HasChildSource, src::HasSource, AdtId, AttrDefId, Lookup, }; #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -94,7 +94,7 @@ impl Attrs { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Attr { - pub(crate) path: Path, + pub(crate) path: ModPath, pub(crate) input: Option, } @@ -106,7 +106,7 @@ pub enum AttrInput { impl Attr { fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option { - let path = Path::from_src(ast.path()?, hygiene)?; + let path = ModPath::from_src(ast.path()?, hygiene)?; let input = match ast.input() { None => None, Some(ast::AttrInput::Literal(lit)) => { diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index b3bc336cf..7787cb87f 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -83,7 +83,7 @@ impl Expander { fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option { self.crate_def_map - .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) + .resolve_path(db, self.module.local_id, path.mod_path(), BuiltinShadowMode::Other) .0 .take_macros() } diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index bd237a7b3..9aae7e48e 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -74,7 +74,7 @@ use crate::{ builtin_type::BuiltinType, db::DefDatabase, nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, - path::Path, + path::ModPath, per_ns::PerNs, AstId, FunctionId, ImplId, LocalImportId, LocalModuleId, ModuleDefId, ModuleId, TraitId, }; @@ -329,7 +329,7 @@ impl CrateDefMap { &self, db: &impl DefDatabase, original_module: LocalModuleId, - path: &Path, + path: &ModPath, shadow: BuiltinShadowMode, ) -> (PerNs, Option) { let res = diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 5d7469a6e..912a073ea 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -22,7 +22,7 @@ use crate::{ diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, Resolution, ResolveMode, }, - path::{Path, PathKind}, + path::{ModPath, PathKind}, per_ns::PerNs, AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, LocalImportId, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, @@ -101,7 +101,7 @@ struct ImportDirective { struct MacroDirective { module_id: LocalModuleId, ast_id: AstId, - path: Path, + path: ModPath, legacy: Option, } @@ -113,7 +113,7 @@ struct DefCollector<'a, DB> { unresolved_imports: Vec, resolved_imports: Vec, unexpanded_macros: Vec, - unexpanded_attribute_macros: Vec<(LocalModuleId, AstId, Path)>, + unexpanded_attribute_macros: Vec<(LocalModuleId, AstId, ModPath)>, mod_dirs: FxHashMap, cfg_options: &'a CfgOptions, } @@ -428,7 +428,7 @@ where } else { match import.path.segments.last() { Some(last_segment) => { - let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); + let name = import.alias.clone().unwrap_or_else(|| last_segment.clone()); log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 @@ -565,7 +565,7 @@ where res } - fn resolve_attribute_macro(&self, path: &Path) -> Option { + fn resolve_attribute_macro(&self, path: &ModPath) -> Option { // FIXME this is currently super hacky, just enough to support the // built-in derives if let Some(name) = path.as_ident() { @@ -829,7 +829,7 @@ where tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok _ => continue, // anything else would be an error (which we currently ignore) }; - let path = Path::from_tt_ident(ident); + let path = ModPath::from_tt_ident(ident); let ast_id = AstId::new(self.file_id, def.kind.ast_id()); self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path)); @@ -917,7 +917,7 @@ where } } -fn is_macro_rules(path: &Path) -> bool { +fn is_macro_rules(path: &ModPath) -> bool { path.as_ident() == Some(&name![macro_rules]) } diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index aab4b1dd9..4a249e7e7 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs @@ -17,7 +17,7 @@ use test_utils::tested_by; use crate::{ db::DefDatabase, nameres::{BuiltinShadowMode, CrateDefMap}, - path::{Path, PathKind}, + path::{ModPath, PathKind}, per_ns::PerNs, AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, }; @@ -69,7 +69,7 @@ impl CrateDefMap { db: &impl DefDatabase, mode: ResolveMode, original_module: LocalModuleId, - path: &Path, + path: &ModPath, shadow: BuiltinShadowMode, ) -> ResolvePathResult { // if it is not the last segment, we prefer the module to the builtin @@ -113,7 +113,7 @@ impl CrateDefMap { None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); - self.resolve_name_in_crate_root_or_extern_prelude(&segment.name, prefer_module(idx)) + self.resolve_name_in_crate_root_or_extern_prelude(&segment, prefer_module(idx)) } PathKind::Plain => { let (idx, segment) = match segments.next() { @@ -121,7 +121,7 @@ impl CrateDefMap { None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; log::debug!("resolving {:?} in module", segment); - self.resolve_name_in_module(db, original_module, &segment.name, prefer_module(idx)) + self.resolve_name_in_module(db, original_module, &segment, prefer_module(idx)) } PathKind::Super => { if let Some(p) = self.modules[original_module].parent { @@ -137,7 +137,7 @@ impl CrateDefMap { Some((_, segment)) => segment, None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; - if let Some(def) = self.extern_prelude.get(&segment.name) { + if let Some(def) = self.extern_prelude.get(&segment) { log::debug!("absolute path {:?} resolved to crate {:?}", path, def); PerNs::types(*def) } else { @@ -168,8 +168,10 @@ impl CrateDefMap { curr_per_ns = match curr { ModuleDefId::ModuleId(module) => { if module.krate != self.krate { - let path = - Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ }; + let path = ModPath { + segments: path.segments[i..].to_vec(), + kind: PathKind::Self_, + }; log::debug!("resolving {:?} in other crate", path); let defp_map = db.crate_def_map(module.krate); let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow); @@ -182,10 +184,10 @@ impl CrateDefMap { } // Since it is a qualified path here, it should not contains legacy macros - match self[module.local_id].scope.get(&segment.name, prefer_module(i)) { + match self[module.local_id].scope.get(&segment, prefer_module(i)) { Some(res) => res.def, _ => { - log::debug!("path segment {:?} not found", segment.name); + log::debug!("path segment {:?} not found", segment); return ResolvePathResult::empty(ReachedFixedPoint::No); } } @@ -194,7 +196,7 @@ impl CrateDefMap { // enum variant tested_by!(can_import_enum_variant); let enum_data = db.enum_data(e); - match enum_data.variant(&segment.name) { + match enum_data.variant(&segment) { Some(local_id) => { let variant = EnumVariantId { parent: e, local_id }; PerNs::both(variant.into(), variant.into()) @@ -214,7 +216,7 @@ impl CrateDefMap { // (`Struct::method`), or some other kind of associated item log::debug!( "path segment {:?} resolved to non-module {:?}, but is not last", - segment.name, + segment, curr, ); diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index a2821e1c3..ecb4d7c03 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -22,7 +22,7 @@ use ra_syntax::{ use test_utils::tested_by; use crate::{ - attr::Attrs, db::DefDatabase, path::Path, trace::Trace, FileAstId, HirFileId, InFile, + attr::Attrs, db::DefDatabase, path::ModPath, trace::Trace, FileAstId, HirFileId, InFile, LocalImportId, }; @@ -154,7 +154,7 @@ pub(super) enum ModuleData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImportData { - pub(super) path: Path, + pub(super) path: ModPath, pub(super) alias: Option, pub(super) is_glob: bool, pub(super) is_prelude: bool, @@ -206,7 +206,7 @@ impl_arena_id!(Macro); #[derive(Debug, PartialEq, Eq)] pub(super) struct MacroData { pub(super) ast_id: FileAstId, - pub(super) path: Path, + pub(super) path: ModPath, pub(super) name: Option, pub(super) export: bool, pub(super) builtin: bool, @@ -327,7 +327,7 @@ impl RawItemsCollector { let attrs = self.parse_attrs(&use_item); let mut buf = Vec::new(); - Path::expand_use_item( + ModPath::expand_use_item( InFile { value: use_item, file_id: self.file_id }, &self.hygiene, |path, use_tree, is_glob, alias| { @@ -353,7 +353,7 @@ impl RawItemsCollector { extern_crate: ast::ExternCrateItem, ) { if let Some(name_ref) = extern_crate.name_ref() { - let path = Path::from_name_ref(&name_ref); + let path = ModPath::from_name_ref(&name_ref); let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); let attrs = self.parse_attrs(&extern_crate); // FIXME: cfg_attr @@ -377,7 +377,7 @@ impl RawItemsCollector { fn add_macro(&mut self, current_module: Option, m: ast::MacroCall) { let attrs = self.parse_attrs(&m); - let path = match m.path().and_then(|path| Path::from_src(path, &self.hygiene)) { + let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) { Some(it) => it, _ => return, }; diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 1e9eb14ea..20d6d98ea 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -1,31 +1,78 @@ //! A desugared representation of paths like `crate::foo` or `::bar`. -mod lower_use; +mod lower; use std::{iter, sync::Arc}; -use either::Either; use hir_expand::{ hygiene::Hygiene, - name::{name, AsName, Name}, + name::{AsName, Name}, }; use ra_db::CrateId; -use ra_syntax::{ - ast::{self, TypeAscriptionOwner}, - AstNode, -}; +use ra_syntax::ast; use crate::{type_ref::TypeRef, InFile}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Path { +pub struct ModPath { pub kind: PathKind, - pub segments: Vec, + pub segments: Vec, +} + +impl ModPath { + pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option { + lower::lower_path(path, hygiene).map(|it| it.mod_path) + } + + pub fn from_simple_segments( + kind: PathKind, + segments: impl IntoIterator, + ) -> ModPath { + let segments = segments.into_iter().collect::>(); + ModPath { kind, segments } + } + + pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath { + name_ref.as_name().into() + } + + /// Converts an `tt::Ident` into a single-identifier `Path`. + pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { + ident.as_name().into() + } + + /// Calls `cb` with all paths, represented by this use item. + pub(crate) fn expand_use_item( + item_src: InFile, + hygiene: &Hygiene, + mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option), + ) { + if let Some(tree) = item_src.value.use_tree() { + lower::lower_use_tree(None, tree, hygiene, &mut cb); + } + } + + pub fn is_ident(&self) -> bool { + self.kind == PathKind::Plain && self.segments.len() == 1 + } + + pub fn is_self(&self) -> bool { + self.kind == PathKind::Self_ && self.segments.is_empty() + } + + /// If this path is a single identifier, like `foo`, return its name. + pub fn as_ident(&self) -> Option<&Name> { + if self.kind != PathKind::Plain || self.segments.len() > 1 { + return None; + } + self.segments.first() + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PathSegment { - pub name: Name, - pub args_and_bindings: Option>, +pub struct Path { + mod_path: ModPath, + /// Invariant: the same len as self.path.segments + generic_args: Vec>>, } /// Generic arguments to a path segment (e.g. the `i32` in `Option`). This @@ -65,221 +112,110 @@ pub enum PathKind { } impl Path { - /// Calls `cb` with all paths, represented by this use item. - pub(crate) fn expand_use_item( - item_src: InFile, - hygiene: &Hygiene, - mut cb: impl FnMut(Path, &ast::UseTree, bool, Option), - ) { - if let Some(tree) = item_src.value.use_tree() { - lower_use::lower_use_tree(None, tree, hygiene, &mut cb); - } - } - - pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator) -> Path { - Path { - kind, - segments: segments - .into_iter() - .map(|name| PathSegment { name, args_and_bindings: None }) - .collect(), - } - } - /// Converts an `ast::Path` to `Path`. Works with use trees. /// DEPRECATED: It does not handle `$crate` from macro call. pub fn from_ast(path: ast::Path) -> Option { - Path::from_src(path, &Hygiene::new_unhygienic()) + lower::lower_path(path, &Hygiene::new_unhygienic()) } /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. - pub fn from_src(mut path: ast::Path, hygiene: &Hygiene) -> Option { - let mut kind = PathKind::Plain; - let mut segments = Vec::new(); - loop { - let segment = path.segment()?; - - if segment.has_colon_colon() { - kind = PathKind::Abs; - } - - match segment.kind()? { - ast::PathSegmentKind::Name(name_ref) => { - // FIXME: this should just return name - match hygiene.name_ref_to_name(name_ref) { - Either::Left(name) => { - let args = segment - .type_arg_list() - .and_then(GenericArgs::from_ast) - .or_else(|| { - GenericArgs::from_fn_like_path_ast( - segment.param_list(), - segment.ret_type(), - ) - }) - .map(Arc::new); - let segment = PathSegment { name, args_and_bindings: args }; - segments.push(segment); - } - Either::Right(crate_id) => { - kind = PathKind::DollarCrate(crate_id); - break; - } - } - } - ast::PathSegmentKind::Type { type_ref, trait_ref } => { - assert!(path.qualifier().is_none()); // this can only occur at the first segment - - let self_type = TypeRef::from_ast(type_ref?); - - match trait_ref { - // ::foo - None => { - kind = PathKind::Type(Box::new(self_type)); - } - // >::Foo desugars to Trait::Foo - Some(trait_ref) => { - let path = Path::from_src(trait_ref.path()?, hygiene)?; - kind = path.kind; - let mut prefix_segments = path.segments; - prefix_segments.reverse(); - segments.extend(prefix_segments); - // Insert the type reference (T in the above example) as Self parameter for the trait - let mut last_segment = segments.last_mut()?; - if last_segment.args_and_bindings.is_none() { - last_segment.args_and_bindings = - Some(Arc::new(GenericArgs::empty())); - }; - let args = last_segment.args_and_bindings.as_mut().unwrap(); - let mut args_inner = Arc::make_mut(args); - args_inner.has_self_type = true; - args_inner.args.insert(0, GenericArg::Type(self_type)); - } - } - } - ast::PathSegmentKind::CrateKw => { - kind = PathKind::Crate; - break; - } - ast::PathSegmentKind::SelfKw => { - kind = PathKind::Self_; - break; - } - ast::PathSegmentKind::SuperKw => { - kind = PathKind::Super; - break; - } - } - path = match qualifier(&path) { - Some(it) => it, - None => break, - }; - } - segments.reverse(); - return Some(Path { kind, segments }); - - fn qualifier(path: &ast::Path) -> Option { - if let Some(q) = path.qualifier() { - return Some(q); - } - // FIXME: this bottom up traversal is not too precise. - // Should we handle do a top-down analysis, recording results? - let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; - let use_tree = use_tree_list.parent_use_tree(); - use_tree.path() - } + pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option { + lower::lower_path(path, hygiene) } /// Converts an `ast::NameRef` into a single-identifier `Path`. pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path { - name_ref.as_name().into() + Path { mod_path: name_ref.as_name().into(), generic_args: vec![None] } } - /// Converts an `tt::Ident` into a single-identifier `Path`. - pub(crate) fn from_tt_ident(ident: &tt::Ident) -> Path { - ident.as_name().into() + /// `true` if this path is just a standalone `self` + pub fn is_self(&self) -> bool { + self.mod_path.is_self() } - /// `true` is this path is a single identifier, like `foo` - pub fn is_ident(&self) -> bool { - self.kind == PathKind::Plain && self.segments.len() == 1 + pub fn kind(&self) -> &PathKind { + &self.mod_path.kind } - /// `true` if this path is just a standalone `self` - pub fn is_self(&self) -> bool { - self.kind == PathKind::Self_ && self.segments.is_empty() + pub fn segments(&self) -> PathSegments<'_> { + PathSegments { + segments: self.mod_path.segments.as_slice(), + generic_args: self.generic_args.as_slice(), + } } - /// If this path is a single identifier, like `foo`, return its name. - pub fn as_ident(&self) -> Option<&Name> { - if self.kind != PathKind::Plain || self.segments.len() > 1 { + pub fn mod_path(&self) -> &ModPath { + &self.mod_path + } + + pub fn qualifier(&self) -> Option { + if self.mod_path.is_ident() { return None; } - self.segments.first().map(|s| &s.name) + let res = Path { + mod_path: ModPath { + kind: self.mod_path.kind.clone(), + segments: self.mod_path.segments[..self.mod_path.segments.len() - 1].to_vec(), + }, + generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(), + }; + Some(res) } +} - pub fn expand_macro_expr(&self) -> Option { - self.as_ident().and_then(|name| Some(name.clone())) - } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathSegment<'a> { + pub name: &'a Name, + pub args_and_bindings: Option<&'a GenericArgs>, +} - pub fn is_type_relative(&self) -> bool { - match self.kind { - PathKind::Type(_) => true, - _ => false, - } +pub struct PathSegments<'a> { + segments: &'a [Name], + generic_args: &'a [Option>], +} + +impl<'a> PathSegments<'a> { + pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] }; + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn len(&self) -> usize { + self.segments.len() + } + pub fn first(&self) -> Option> { + self.get(0) + } + pub fn last(&self) -> Option> { + self.get(self.len().checked_sub(1)?) + } + pub fn get(&self, idx: usize) -> Option> { + assert_eq!(self.segments.len(), self.generic_args.len()); + let res = PathSegment { + name: self.segments.get(idx)?, + args_and_bindings: self.generic_args.get(idx).unwrap().as_ref().map(|it| &**it), + }; + Some(res) + } + pub fn skip(&self, len: usize) -> PathSegments<'a> { + assert_eq!(self.segments.len(), self.generic_args.len()); + PathSegments { segments: &self.segments[len..], generic_args: &self.generic_args[len..] } + } + pub fn take(&self, len: usize) -> PathSegments<'a> { + assert_eq!(self.segments.len(), self.generic_args.len()); + PathSegments { segments: &self.segments[..len], generic_args: &self.generic_args[..len] } + } + pub fn iter(&self) -> impl Iterator> { + self.segments.iter().zip(self.generic_args.iter()).map(|(name, args)| PathSegment { + name, + args_and_bindings: args.as_ref().map(|it| &**it), + }) } } impl GenericArgs { pub(crate) fn from_ast(node: ast::TypeArgList) -> Option { - let mut args = Vec::new(); - for type_arg in node.type_args() { - let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); - args.push(GenericArg::Type(type_ref)); - } - // lifetimes ignored for now - let mut bindings = Vec::new(); - for assoc_type_arg in node.assoc_type_args() { - if let Some(name_ref) = assoc_type_arg.name_ref() { - let name = name_ref.as_name(); - let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref()); - bindings.push((name, type_ref)); - } - } - if args.is_empty() && bindings.is_empty() { - None - } else { - Some(GenericArgs { args, has_self_type: false, bindings }) - } - } - - /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) - /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). - pub(crate) fn from_fn_like_path_ast( - params: Option, - ret_type: Option, - ) -> Option { - let mut args = Vec::new(); - let mut bindings = Vec::new(); - if let Some(params) = params { - let mut param_types = Vec::new(); - for param in params.params() { - let type_ref = TypeRef::from_ast_opt(param.ascribed_type()); - param_types.push(type_ref); - } - let arg = GenericArg::Type(TypeRef::Tuple(param_types)); - args.push(arg); - } - if let Some(ret_type) = ret_type { - let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); - bindings.push((name![Output], type_ref)) - } - if args.is_empty() && bindings.is_empty() { - None - } else { - Some(GenericArgs { args, has_self_type: false, bindings }) - } + lower::lower_generic_args(node) } pub(crate) fn empty() -> GenericArgs { @@ -289,7 +225,16 @@ impl GenericArgs { impl From for Path { fn from(name: Name) -> Path { - Path::from_simple_segments(PathKind::Plain, iter::once(name)) + Path { + mod_path: ModPath::from_simple_segments(PathKind::Plain, iter::once(name)), + generic_args: vec![None], + } + } +} + +impl From for ModPath { + fn from(name: Name) -> ModPath { + ModPath::from_simple_segments(PathKind::Plain, iter::once(name)) } } @@ -319,7 +264,7 @@ macro_rules! __known_path { macro_rules! __path { ($start:ident $(:: $seg:ident)*) => ({ $crate::__known_path!($start $(:: $seg)*); - $crate::path::Path::from_simple_segments($crate::path::PathKind::Abs, vec![ + $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec![ $crate::path::__name![$start], $($crate::path::__name![$seg],)* ]) }); diff --git a/crates/ra_hir_def/src/path/lower.rs b/crates/ra_hir_def/src/path/lower.rs new file mode 100644 index 000000000..a2e995198 --- /dev/null +++ b/crates/ra_hir_def/src/path/lower.rs @@ -0,0 +1,176 @@ +//! Transforms syntax into `Path` objects, ideally with accounting for hygiene + +mod lower_use; + +use std::sync::Arc; + +use either::Either; +use hir_expand::{ + hygiene::Hygiene, + name::{name, AsName}, +}; +use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner}; + +use crate::{ + path::{GenericArg, GenericArgs, ModPath, Path, PathKind}, + type_ref::TypeRef, +}; + +pub(super) use lower_use::lower_use_tree; + +/// Converts an `ast::Path` to `Path`. Works with use trees. +/// It correctly handles `$crate` based path from macro call. +pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option { + let mut kind = PathKind::Plain; + let mut segments = Vec::new(); + let mut generic_args = Vec::new(); + loop { + let segment = path.segment()?; + + if segment.has_colon_colon() { + kind = PathKind::Abs; + } + + match segment.kind()? { + ast::PathSegmentKind::Name(name_ref) => { + // FIXME: this should just return name + match hygiene.name_ref_to_name(name_ref) { + Either::Left(name) => { + let args = segment + .type_arg_list() + .and_then(lower_generic_args) + .or_else(|| { + lower_generic_args_from_fn_path( + segment.param_list(), + segment.ret_type(), + ) + }) + .map(Arc::new); + segments.push(name); + generic_args.push(args) + } + Either::Right(crate_id) => { + kind = PathKind::DollarCrate(crate_id); + break; + } + } + } + ast::PathSegmentKind::Type { type_ref, trait_ref } => { + assert!(path.qualifier().is_none()); // this can only occur at the first segment + + let self_type = TypeRef::from_ast(type_ref?); + + match trait_ref { + // ::foo + None => { + kind = PathKind::Type(Box::new(self_type)); + } + // >::Foo desugars to Trait::Foo + Some(trait_ref) => { + let path = Path::from_src(trait_ref.path()?, hygiene)?; + kind = path.mod_path.kind; + + let mut prefix_segments = path.mod_path.segments; + prefix_segments.reverse(); + segments.extend(prefix_segments); + + let mut prefix_args = path.generic_args; + prefix_args.reverse(); + generic_args.extend(prefix_args); + + // Insert the type reference (T in the above example) as Self parameter for the trait + let last_segment = generic_args.last_mut()?; + if last_segment.is_none() { + *last_segment = Some(Arc::new(GenericArgs::empty())); + }; + let args = last_segment.as_mut().unwrap(); + let mut args_inner = Arc::make_mut(args); + args_inner.has_self_type = true; + args_inner.args.insert(0, GenericArg::Type(self_type)); + } + } + } + ast::PathSegmentKind::CrateKw => { + kind = PathKind::Crate; + break; + } + ast::PathSegmentKind::SelfKw => { + kind = PathKind::Self_; + break; + } + ast::PathSegmentKind::SuperKw => { + kind = PathKind::Super; + break; + } + } + path = match qualifier(&path) { + Some(it) => it, + None => break, + }; + } + segments.reverse(); + generic_args.reverse(); + let mod_path = ModPath { kind, segments }; + return Some(Path { mod_path, generic_args }); + + fn qualifier(path: &ast::Path) -> Option { + if let Some(q) = path.qualifier() { + return Some(q); + } + // FIXME: this bottom up traversal is not too precise. + // Should we handle do a top-down analysis, recording results? + let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; + let use_tree = use_tree_list.parent_use_tree(); + use_tree.path() + } +} + +pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option { + let mut args = Vec::new(); + for type_arg in node.type_args() { + let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); + args.push(GenericArg::Type(type_ref)); + } + // lifetimes ignored for now + let mut bindings = Vec::new(); + for assoc_type_arg in node.assoc_type_args() { + if let Some(name_ref) = assoc_type_arg.name_ref() { + let name = name_ref.as_name(); + let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref()); + bindings.push((name, type_ref)); + } + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } +} + +/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) +/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). +fn lower_generic_args_from_fn_path( + params: Option, + ret_type: Option, +) -> Option { + let mut args = Vec::new(); + let mut bindings = Vec::new(); + if let Some(params) = params { + let mut param_types = Vec::new(); + for param in params.params() { + let type_ref = TypeRef::from_ast_opt(param.ascribed_type()); + param_types.push(type_ref); + } + let arg = GenericArg::Type(TypeRef::Tuple(param_types)); + args.push(arg); + } + if let Some(ret_type) = ret_type { + let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); + bindings.push((name![Output], type_ref)) + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } +} diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs new file mode 100644 index 000000000..ea3fdb56c --- /dev/null +++ b/crates/ra_hir_def/src/path/lower/lower_use.rs @@ -0,0 +1,112 @@ +//! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like +//! `foo::bar`, `foo::baz`; + +use std::iter; + +use either::Either; +use hir_expand::{ + hygiene::Hygiene, + name::{AsName, Name}, +}; +use ra_syntax::ast::{self, NameOwner}; + +use crate::path::{ModPath, PathKind}; + +pub(crate) fn lower_use_tree( + prefix: Option, + tree: ast::UseTree, + hygiene: &Hygiene, + cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option), +) { + if let Some(use_tree_list) = tree.use_tree_list() { + let prefix = match tree.path() { + // E.g. use something::{{{inner}}}; + 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, hygiene) { + Some(it) => Some(it), + None => return, // FIXME: report errors somewhere + }, + }; + for child_tree in use_tree_list.use_trees() { + lower_use_tree(prefix.clone(), child_tree, hygiene, cb); + } + } else { + let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name()); + if let Some(ast_path) = tree.path() { + // Handle self in a path. + // E.g. `use something::{self, <...>}` + if ast_path.qualifier().is_none() { + if let Some(segment) = ast_path.segment() { + if segment.kind() == Some(ast::PathSegmentKind::SelfKw) { + if let Some(prefix) = prefix { + cb(prefix, &tree, false, alias); + return; + } + } + } + } + if let Some(path) = convert_path(prefix, ast_path, hygiene) { + let is_glob = tree.has_star(); + cb(path, &tree, is_glob, alias) + } + // FIXME: report errors somewhere + // We get here if we do + } + } +} + +fn convert_path(prefix: Option, path: ast::Path, hygiene: &Hygiene) -> Option { + let prefix = if let Some(qual) = path.qualifier() { + Some(convert_path(prefix, qual, hygiene)?) + } else { + prefix + }; + + let segment = path.segment()?; + let res = match segment.kind()? { + ast::PathSegmentKind::Name(name_ref) => { + match hygiene.name_ref_to_name(name_ref) { + Either::Left(name) => { + // no type args in use + let mut res = prefix.unwrap_or_else(|| ModPath { + kind: PathKind::Plain, + segments: Vec::with_capacity(1), + }); + res.segments.push(name); + res + } + Either::Right(crate_id) => { + return Some(ModPath::from_simple_segments( + PathKind::DollarCrate(crate_id), + iter::empty(), + )) + } + } + } + ast::PathSegmentKind::CrateKw => { + if prefix.is_some() { + return None; + } + ModPath::from_simple_segments(PathKind::Crate, iter::empty()) + } + ast::PathSegmentKind::SelfKw => { + if prefix.is_some() { + return None; + } + ModPath::from_simple_segments(PathKind::Self_, iter::empty()) + } + ast::PathSegmentKind::SuperKw => { + if prefix.is_some() { + return None; + } + ModPath::from_simple_segments(PathKind::Super, iter::empty()) + } + ast::PathSegmentKind::Type { .. } => { + // not allowed in imports + return None; + } + }; + Some(res) +} diff --git a/crates/ra_hir_def/src/path/lower_use.rs b/crates/ra_hir_def/src/path/lower_use.rs deleted file mode 100644 index e2e1f716d..000000000 --- a/crates/ra_hir_def/src/path/lower_use.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like -//! `foo::bar`, `foo::baz`; - -use std::iter; - -use either::Either; -use hir_expand::{ - hygiene::Hygiene, - name::{AsName, Name}, -}; -use ra_syntax::ast::{self, NameOwner}; - -use crate::path::{Path, PathKind, PathSegment}; - -pub(crate) fn lower_use_tree( - prefix: Option, - tree: ast::UseTree, - hygiene: &Hygiene, - cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option), -) { - if let Some(use_tree_list) = tree.use_tree_list() { - let prefix = match tree.path() { - // E.g. use something::{{{inner}}}; - 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, hygiene) { - Some(it) => Some(it), - None => return, // FIXME: report errors somewhere - }, - }; - for child_tree in use_tree_list.use_trees() { - lower_use_tree(prefix.clone(), child_tree, hygiene, cb); - } - } else { - let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name()); - if let Some(ast_path) = tree.path() { - // Handle self in a path. - // E.g. `use something::{self, <...>}` - if ast_path.qualifier().is_none() { - if let Some(segment) = ast_path.segment() { - if segment.kind() == Some(ast::PathSegmentKind::SelfKw) { - if let Some(prefix) = prefix { - cb(prefix, &tree, false, alias); - return; - } - } - } - } - if let Some(path) = convert_path(prefix, ast_path, hygiene) { - let is_glob = tree.has_star(); - cb(path, &tree, is_glob, alias) - } - // FIXME: report errors somewhere - // We get here if we do - } - } -} - -fn convert_path(prefix: Option, path: ast::Path, hygiene: &Hygiene) -> Option { - let prefix = if let Some(qual) = path.qualifier() { - Some(convert_path(prefix, qual, hygiene)?) - } else { - prefix - }; - - let segment = path.segment()?; - let res = match segment.kind()? { - ast::PathSegmentKind::Name(name_ref) => { - match hygiene.name_ref_to_name(name_ref) { - Either::Left(name) => { - // no type args in use - let mut res = prefix.unwrap_or_else(|| Path { - kind: PathKind::Plain, - segments: Vec::with_capacity(1), - }); - res.segments.push(PathSegment { - name, - args_and_bindings: None, // no type args in use - }); - res - } - Either::Right(crate_id) => { - return Some(Path::from_simple_segments( - PathKind::DollarCrate(crate_id), - iter::empty(), - )) - } - } - } - ast::PathSegmentKind::CrateKw => { - if prefix.is_some() { - return None; - } - Path::from_simple_segments(PathKind::Crate, iter::empty()) - } - ast::PathSegmentKind::SelfKw => { - if prefix.is_some() { - return None; - } - Path::from_simple_segments(PathKind::Self_, iter::empty()) - } - ast::PathSegmentKind::SuperKw => { - if prefix.is_some() { - return None; - } - Path::from_simple_segments(PathKind::Super, iter::empty()) - } - ast::PathSegmentKind::Type { .. } => { - // not allowed in imports - return None; - } - }; - Some(res) -} diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index b6d595a20..2694c0438 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -15,7 +15,7 @@ use crate::{ expr::{ExprId, PatId}, generics::GenericParams, nameres::{BuiltinShadowMode, CrateDefMap}, - path::{Path, PathKind}, + path::{ModPath, PathKind}, per_ns::PerNs, AdtId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, @@ -91,7 +91,7 @@ pub enum ValueNs { impl Resolver { /// Resolve known trait from std, like `std::futures::Future` - pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &Path) -> Option { + pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &ModPath) -> Option { let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; match res { ModuleDefId::TraitId(it) => Some(it), @@ -100,7 +100,7 @@ impl Resolver { } /// Resolve known struct from std, like `std::boxed::Box` - pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &Path) -> Option { + pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &ModPath) -> Option { let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; match res { ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it), @@ -109,7 +109,7 @@ impl Resolver { } /// Resolve known enum from std, like `std::result::Result` - pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &Path) -> Option { + pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &ModPath) -> Option { let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; match res { ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it), @@ -120,33 +120,30 @@ impl Resolver { fn resolve_module_path( &self, db: &impl DefDatabase, - path: &Path, + path: &ModPath, shadow: BuiltinShadowMode, ) -> PerNs { let (item_map, module) = match self.module() { Some(it) => it, None => return PerNs::none(), }; - let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow); + let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow); if segment_index.is_some() { return PerNs::none(); } module_res } - pub fn resolve_module_path_in_items(&self, db: &impl DefDatabase, path: &Path) -> PerNs { + pub fn resolve_module_path_in_items(&self, db: &impl DefDatabase, path: &ModPath) -> PerNs { self.resolve_module_path(db, path, BuiltinShadowMode::Module) } pub fn resolve_path_in_type_ns( &self, db: &impl DefDatabase, - path: &Path, + path: &ModPath, ) -> Option<(TypeNs, Option)> { - if path.is_type_relative() { - return None; - } - let first_name = &path.segments.first()?.name; + let first_name = path.segments.first()?; let skip_to_mod = path.kind != PathKind::Plain; for scope in self.scopes.iter().rev() { match scope { @@ -178,7 +175,7 @@ impl Resolver { let (module_def, idx) = m.crate_def_map.resolve_path( db, m.module_id, - path, + &path, BuiltinShadowMode::Other, ); let res = match module_def.take_types()? { @@ -205,7 +202,7 @@ impl Resolver { pub fn resolve_path_in_type_ns_fully( &self, db: &impl DefDatabase, - path: &Path, + path: &ModPath, ) -> Option { let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; if unresolved.is_some() { @@ -214,17 +211,14 @@ impl Resolver { Some(res) } - pub fn resolve_path_in_value_ns<'p>( + pub fn resolve_path_in_value_ns( &self, db: &impl DefDatabase, - path: &'p Path, + path: &ModPath, ) -> Option { - if path.is_type_relative() { - return None; - } let n_segments = path.segments.len(); let tmp = name![self]; - let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name }; + let first_name = if path.is_self() { &tmp } else { &path.segments.first()? }; let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); for scope in self.scopes.iter().rev() { match scope { @@ -276,7 +270,7 @@ impl Resolver { let (module_def, idx) = m.crate_def_map.resolve_path( db, m.module_id, - path, + &path, BuiltinShadowMode::Other, ); return match idx { @@ -322,7 +316,7 @@ impl Resolver { pub fn resolve_path_in_value_ns_fully( &self, db: &impl DefDatabase, - path: &Path, + path: &ModPath, ) -> Option { match self.resolve_path_in_value_ns(db, path)? { ResolveValueResult::ValueNs(it) => Some(it), @@ -330,9 +324,13 @@ impl Resolver { } } - pub fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option { + pub fn resolve_path_as_macro( + &self, + db: &impl DefDatabase, + path: &ModPath, + ) -> Option { let (item_map, module) = self.module()?; - item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros() + item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() } pub fn process_all_names(&self, db: &impl DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { -- cgit v1.2.3