Use different types for path with and without generics

This commit is contained in:
Aleksey Kladov 2019-12-13 12:12:36 +01:00
parent f720855e1e
commit 2619950b3b
18 changed files with 473 additions and 356 deletions

View file

@ -578,7 +578,7 @@ fn apply_auto_import(
fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
let mut ps = Vec::<SmolStr>::with_capacity(10);
match path.kind {
match path.kind() {
hir::PathKind::Abs => ps.push("".into()),
hir::PathKind::Crate => ps.push("crate".into()),
hir::PathKind::Plain => {}
@ -586,9 +586,7 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
hir::PathKind::Super => ps.push("super".into()),
hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None,
}
for s in path.segments.iter() {
ps.push(s.name.to_string().into());
}
ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
Some(ps)
}

View file

@ -54,7 +54,7 @@ pub use hir_def::{
builtin_type::BuiltinType,
docs::Documentation,
nameres::ModuleSource,
path::{Path, PathKind},
path::{ModPath, Path, PathKind},
type_ref::Mutability,
};
pub use hir_expand::{

View file

@ -258,7 +258,7 @@ impl SourceAnalyzer {
) -> Option<MacroDef> {
let hygiene = Hygiene::new(db, macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?;
self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into())
self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into())
}
pub fn resolve_hir_path(
@ -266,40 +266,42 @@ impl SourceAnalyzer {
db: &impl HirDatabase,
path: &crate::Path,
) -> Option<PathResolution> {
let types = self.resolver.resolve_path_in_type_ns_fully(db, &path).map(|ty| match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
PathResolution::Def(Adt::from(it).into())
}
TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
});
let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(pat_id) => {
let var = Local { parent: self.body_owner?, pat_id };
PathResolution::Local(var)
let types =
self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
PathResolution::Def(Adt::from(it).into())
}
ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
};
Some(res)
});
TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
});
let values =
self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(pat_id) => {
let var = Local { parent: self.body_owner?, pat_id };
PathResolution::Local(var)
}
ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
};
Some(res)
});
let items = self
.resolver
.resolve_module_path_in_items(db, &path)
.resolve_module_path_in_items(db, path.mod_path())
.take_types()
.map(|it| PathResolution::Def(it.into()));
types.or(values).or(items).or_else(|| {
self.resolver
.resolve_path_as_macro(db, &path)
.resolve_path_as_macro(db, path.mod_path())
.map(|def| PathResolution::Macro(def.into()))
})
}

View file

@ -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<AttrInput>,
}
@ -106,7 +106,7 @@ pub enum AttrInput {
impl Attr {
fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
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)) => {

View file

@ -83,7 +83,7 @@ impl Expander {
fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option<MacroDefId> {
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()
}

View file

@ -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<usize>) {
let res =

View file

@ -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<ast::MacroCall>,
path: Path,
path: ModPath,
legacy: Option<MacroCallId>,
}
@ -113,7 +113,7 @@ struct DefCollector<'a, DB> {
unresolved_imports: Vec<ImportDirective>,
resolved_imports: Vec<ImportDirective>,
unexpanded_macros: Vec<MacroDirective>,
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>,
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>,
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
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<MacroDefId> {
fn resolve_attribute_macro(&self, path: &ModPath) -> Option<MacroDefId> {
// 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])
}

View file

@ -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,
);

View file

@ -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<Name>,
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<ast::MacroCall>,
pub(super) path: Path,
pub(super) path: ModPath,
pub(super) name: Option<Name>,
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<Module>, 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,
};

View file

@ -1,31 +1,78 @@
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::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<PathSegment>,
pub segments: Vec<Name>,
}
impl ModPath {
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
lower::lower_path(path, hygiene).map(|it| it.mod_path)
}
pub fn from_simple_segments(
kind: PathKind,
segments: impl IntoIterator<Item = Name>,
) -> ModPath {
let segments = segments.into_iter().collect::<Vec<_>>();
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<ast::UseItem>,
hygiene: &Hygiene,
mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<Name>),
) {
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<Arc<GenericArgs>>,
pub struct Path {
mod_path: ModPath,
/// Invariant: the same len as self.path.segments
generic_args: Vec<Option<Arc<GenericArgs>>>,
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). 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<ast::UseItem>,
hygiene: &Hygiene,
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
) {
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<Item = Name>) -> 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> {
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<Path> {
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 {
// <T>::foo
None => {
kind = PathKind::Type(Box::new(self_type));
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::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<ast::Path> {
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<Path> {
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()
}
/// Converts an `tt::Ident` into a single-identifier `Path`.
pub(crate) fn from_tt_ident(ident: &tt::Ident) -> Path {
ident.as_name().into()
}
/// `true` is this path is a single identifier, like `foo`
pub fn is_ident(&self) -> bool {
self.kind == PathKind::Plain && self.segments.len() == 1
Path { mod_path: name_ref.as_name().into(), generic_args: vec![None] }
}
/// `true` if this path is just a standalone `self`
pub fn is_self(&self) -> bool {
self.kind == PathKind::Self_ && self.segments.is_empty()
self.mod_path.is_self()
}
/// 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 kind(&self) -> &PathKind {
&self.mod_path.kind
}
pub fn segments(&self) -> PathSegments<'_> {
PathSegments {
segments: self.mod_path.segments.as_slice(),
generic_args: self.generic_args.as_slice(),
}
}
pub fn mod_path(&self) -> &ModPath {
&self.mod_path
}
pub fn qualifier(&self) -> Option<Path> {
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<Name> {
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 struct PathSegments<'a> {
segments: &'a [Name],
generic_args: &'a [Option<Arc<GenericArgs>>],
}
impl<'a> PathSegments<'a> {
pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] };
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_type_relative(&self) -> bool {
match self.kind {
PathKind::Type(_) => true,
_ => false,
}
pub fn len(&self) -> usize {
self.segments.len()
}
pub fn first(&self) -> Option<PathSegment<'a>> {
self.get(0)
}
pub fn last(&self) -> Option<PathSegment<'a>> {
self.get(self.len().checked_sub(1)?)
}
pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
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<Item = PathSegment<'a>> {
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<GenericArgs> {
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<ast::ParamList>,
ret_type: Option<ast::RetType>,
) -> Option<GenericArgs> {
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<Name> 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<Name> 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],)*
])
});

View file

@ -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<Path> {
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 {
// <T>::foo
None => {
kind = PathKind::Type(Box::new(self_type));
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::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<ast::Path> {
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<GenericArgs> {
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<ast::ParamList>,
ret_type: Option<ast::RetType>,
) -> Option<GenericArgs> {
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 })
}
}

View file

@ -10,13 +10,13 @@ use hir_expand::{
};
use ra_syntax::ast::{self, NameOwner};
use crate::path::{Path, PathKind, PathSegment};
use crate::path::{ModPath, PathKind};
pub(crate) fn lower_use_tree(
prefix: Option<Path>,
prefix: Option<ModPath>,
tree: ast::UseTree,
hygiene: &Hygiene,
cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option<Name>),
cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<Name>),
) {
if let Some(use_tree_list) = tree.use_tree_list() {
let prefix = match tree.path() {
@ -57,7 +57,7 @@ pub(crate) fn lower_use_tree(
}
}
fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
let prefix = if let Some(qual) = path.qualifier() {
Some(convert_path(prefix, qual, hygiene)?)
} else {
@ -70,18 +70,15 @@ fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Opt
match hygiene.name_ref_to_name(name_ref) {
Either::Left(name) => {
// no type args in use
let mut res = prefix.unwrap_or_else(|| Path {
let mut res = prefix.unwrap_or_else(|| ModPath {
kind: PathKind::Plain,
segments: Vec::with_capacity(1),
});
res.segments.push(PathSegment {
name,
args_and_bindings: None, // no type args in use
});
res.segments.push(name);
res
}
Either::Right(crate_id) => {
return Some(Path::from_simple_segments(
return Some(ModPath::from_simple_segments(
PathKind::DollarCrate(crate_id),
iter::empty(),
))
@ -92,19 +89,19 @@ fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Opt
if prefix.is_some() {
return None;
}
Path::from_simple_segments(PathKind::Crate, iter::empty())
ModPath::from_simple_segments(PathKind::Crate, iter::empty())
}
ast::PathSegmentKind::SelfKw => {
if prefix.is_some() {
return None;
}
Path::from_simple_segments(PathKind::Self_, iter::empty())
ModPath::from_simple_segments(PathKind::Self_, iter::empty())
}
ast::PathSegmentKind::SuperKw => {
if prefix.is_some() {
return None;
}
Path::from_simple_segments(PathKind::Super, iter::empty())
ModPath::from_simple_segments(PathKind::Super, iter::empty())
}
ast::PathSegmentKind::Type { .. } => {
// not allowed in imports

View file

@ -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<TraitId> {
pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &ModPath) -> Option<TraitId> {
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<StructId> {
pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &ModPath) -> Option<StructId> {
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<EnumId> {
pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &ModPath) -> Option<EnumId> {
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<usize>)> {
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<TypeNs> {
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<ResolveValueResult> {
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<ValueNs> {
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<MacroDefId> {
pub fn resolve_path_as_macro(
&self,
db: &impl DefDatabase,
path: &ModPath,
) -> Option<MacroDefId> {
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)) {

View file

@ -386,7 +386,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let resolver = &self.resolver;
// FIXME: this should resolve assoc items as well, see this example:
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
match resolver.resolve_path_in_type_ns_fully(self.db, &path) {
match resolver.resolve_path_in_type_ns_fully(self.db, path.mod_path()) {
Some(TypeNs::AdtId(AdtId::StructId(strukt))) => {
let substs = Ty::substs_from_path(self.db, resolver, path, strukt.into());
let ty = self.db.ty(strukt.into());

View file

@ -32,21 +32,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
path: &Path,
id: ExprOrPatId,
) -> Option<Ty> {
let (value, self_subst) = if let PathKind::Type(type_ref) = &path.kind {
if path.segments.is_empty() {
let (value, self_subst) = if let PathKind::Type(type_ref) = path.kind() {
if path.segments().is_empty() {
// This can't actually happen syntax-wise
return None;
}
let ty = self.make_ty(type_ref);
let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1];
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
self.resolve_ty_assoc_item(
ty,
&path.segments.last().expect("path had at least one segment").name,
&path.segments().last().expect("path had at least one segment").name,
id,
)?
} else {
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, path.mod_path())?;
match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
@ -85,13 +85,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
remaining_index: usize,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
assert!(remaining_index < path.segments.len());
assert!(remaining_index < path.segments().len());
// there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref.
let resolved_segment = &path.segments[remaining_index - 1];
let remaining_segments = &path.segments[remaining_index..];
let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
let remaining_segments = path.segments().skip(remaining_index);
let is_before_last = remaining_segments.len() == 1;
match (def, is_before_last) {
@ -112,7 +112,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
// trait but it's not the last segment, so the next segment
// should resolve to an associated type of that trait (e.g. `<T
// as Iterator>::Item::default`)
let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1];
let remaining_segments_for_ty =
remaining_segments.take(remaining_segments.len() - 1);
let ty = Ty::from_partly_resolved_hir_path(
self.db,
&self.resolver,
@ -138,7 +139,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn resolve_trait_assoc_item(
&mut self,
trait_ref: TraitRef,
segment: &PathSegment,
segment: PathSegment<'_>,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
let trait_ = trait_ref.trait_;
@ -150,7 +151,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
.map(|(_name, id)| (*id).into())
.find_map(|item| match item {
AssocItemId::FunctionId(func) => {
if segment.name == self.db.function_data(func).name {
if segment.name == &self.db.function_data(func).name {
Some(AssocItemId::FunctionId(func))
} else {
None
@ -158,7 +159,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
AssocItemId::ConstId(konst) => {
if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == &segment.name)
if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == segment.name)
{
Some(AssocItemId::ConstId(konst))
} else {

View file

@ -11,7 +11,7 @@ use std::sync::Arc;
use hir_def::{
builtin_type::BuiltinType,
generics::WherePredicate,
path::{GenericArg, Path, PathKind, PathSegment},
path::{GenericArg, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
type_ref::{TypeBound, TypeRef},
AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId,
@ -101,13 +101,13 @@ impl Ty {
TypeRef::Path(path) => path,
_ => return None,
};
if let PathKind::Type(_) = &path.kind {
if let PathKind::Type(_) = path.kind() {
return None;
}
if path.segments.len() > 1 {
if path.segments().len() > 1 {
return None;
}
let resolution = match resolver.resolve_path_in_type_ns(db, path) {
let resolution = match resolver.resolve_path_in_type_ns(db, path.mod_path()) {
Some((it, None)) => it,
_ => return None,
};
@ -124,11 +124,11 @@ impl Ty {
db: &impl HirDatabase,
resolver: &Resolver,
ty: Ty,
remaining_segments: &[PathSegment],
remaining_segments: PathSegments<'_>,
) -> Ty {
if remaining_segments.len() == 1 {
// resolve unselected assoc types
let segment = &remaining_segments[0];
let segment = remaining_segments.first().unwrap();
Ty::select_associated_type(db, resolver, ty, segment)
} else if remaining_segments.len() > 1 {
// FIXME report error (ambiguous associated type)
@ -142,15 +142,15 @@ impl Ty {
db: &impl HirDatabase,
resolver: &Resolver,
resolution: TypeNs,
resolved_segment: &PathSegment,
remaining_segments: &[PathSegment],
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
) -> Ty {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
let trait_ref =
TraitRef::from_resolved_path(db, resolver, trait_, resolved_segment, None);
return if remaining_segments.len() == 1 {
let segment = &remaining_segments[0];
let segment = remaining_segments.first().unwrap();
let associated_ty = associated_type_by_name_including_super_traits(
db,
trait_ref.trait_,
@ -202,21 +202,21 @@ impl Ty {
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
// Resolve the path (in type namespace)
if let PathKind::Type(type_ref) = &path.kind {
if let PathKind::Type(type_ref) = path.kind() {
let ty = Ty::from_hir(db, resolver, &type_ref);
let remaining_segments = &path.segments[..];
return Ty::from_type_relative_path(db, resolver, ty, remaining_segments);
return Ty::from_type_relative_path(db, resolver, ty, path.segments());
}
let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
Some(it) => it,
None => return Ty::Unknown,
};
let (resolution, remaining_index) =
match resolver.resolve_path_in_type_ns(db, path.mod_path()) {
Some(it) => it,
None => return Ty::Unknown,
};
let (resolved_segment, remaining_segments) = match remaining_index {
None => (
path.segments.last().expect("resolved path has at least one element"),
&[] as &[PathSegment],
path.segments().last().expect("resolved path has at least one element"),
PathSegments::EMPTY,
),
Some(i) => (&path.segments[i - 1], &path.segments[i..]),
Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
};
Ty::from_partly_resolved_hir_path(
db,
@ -231,7 +231,7 @@ impl Ty {
db: &impl HirDatabase,
resolver: &Resolver,
self_ty: Ty,
segment: &PathSegment,
segment: PathSegment<'_>,
) -> Ty {
let param_idx = match self_ty {
Ty::Param { idx, .. } => idx,
@ -261,7 +261,7 @@ impl Ty {
fn from_hir_path_inner(
db: &impl HirDatabase,
resolver: &Resolver,
segment: &PathSegment,
segment: PathSegment<'_>,
typable: TyDefId,
) -> Ty {
let generic_def = match typable {
@ -284,7 +284,7 @@ impl Ty {
// special-case enum variants
resolved: ValueTyDefId,
) -> Substs {
let last = path.segments.last().expect("path should have at least one segment");
let last = path.segments().last().expect("path should have at least one segment");
let (segment, generic_def) = match resolved {
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
ValueTyDefId::StructId(it) => (last, Some(it.into())),
@ -296,13 +296,11 @@ impl Ty {
// referring to the variant. So `Option::<T>::None` and
// `Option::None::<T>` are both allowed (though the former is
// preferred). See also `def_ids_for_path_segments` in rustc.
let len = path.segments.len();
let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() {
// Option::<T>::None
&path.segments[len - 2]
} else {
// Option::None::<T>
last
let len = path.segments().len();
let penultimate = if len >= 2 { path.segments().get(len - 2) } else { None };
let segment = match penultimate {
Some(segment) if segment.args_and_bindings.is_some() => segment,
_ => last,
};
(segment, Some(var.parent.into()))
}
@ -314,7 +312,7 @@ impl Ty {
pub(super) fn substs_from_path_segment(
db: &impl HirDatabase,
resolver: &Resolver,
segment: &PathSegment,
segment: PathSegment<'_>,
def_generic: Option<GenericDefId>,
add_self_param: bool,
) -> Substs {
@ -372,11 +370,11 @@ impl TraitRef {
path: &Path,
explicit_self_ty: Option<Ty>,
) -> Option<Self> {
let resolved = match resolver.resolve_path_in_type_ns_fully(db, &path)? {
let resolved = match resolver.resolve_path_in_type_ns_fully(db, path.mod_path())? {
TypeNs::TraitId(tr) => tr,
_ => return None,
};
let segment = path.segments.last().expect("path should have at least one segment");
let segment = path.segments().last().expect("path should have at least one segment");
Some(TraitRef::from_resolved_path(db, resolver, resolved.into(), segment, explicit_self_ty))
}
@ -384,7 +382,7 @@ impl TraitRef {
db: &impl HirDatabase,
resolver: &Resolver,
resolved: TraitId,
segment: &PathSegment,
segment: PathSegment<'_>,
explicit_self_ty: Option<Ty>,
) -> Self {
let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved);
@ -410,7 +408,7 @@ impl TraitRef {
fn substs_from_path(
db: &impl HirDatabase,
resolver: &Resolver,
segment: &PathSegment,
segment: PathSegment<'_>,
resolved: TraitId,
) -> Substs {
let has_self_param =
@ -464,12 +462,12 @@ fn assoc_type_bindings_from_type_bound<'a>(
trait_ref: TraitRef,
) -> impl Iterator<Item = GenericPredicate> + 'a {
let last_segment = match bound {
TypeBound::Path(path) => path.segments.last(),
TypeBound::Path(path) => path.segments().last(),
TypeBound::Error => None,
};
last_segment
.into_iter()
.flat_map(|segment| segment.args_and_bindings.iter())
.flat_map(|segment| segment.args_and_bindings.into_iter())
.flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
.map(move |(name, type_ref)| {
let associated_ty =

View file

@ -6,6 +6,7 @@ use hir_def::{
adt::VariantData,
db::DefDatabase,
generics::{GenericParams, TypeParamData},
path::Path,
resolver::{HasResolver, TypeNs},
type_ref::TypeRef,
ContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId,
@ -22,10 +23,10 @@ fn direct_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec<TraitId> {
.where_predicates
.iter()
.filter_map(|pred| match &pred.type_ref {
TypeRef::Path(p) if p.as_ident() == Some(&name![Self]) => pred.bound.as_path(),
TypeRef::Path(p) if p == &Path::from(name![Self]) => pred.bound.as_path(),
_ => None,
})
.filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) {
.filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
Some(TypeNs::TraitId(t)) => Some(t),
_ => None,
})

View file

@ -188,10 +188,9 @@ impl<'a> CompletionContext<'a> {
self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
self.has_type_args = segment.type_arg_list().is_some();
if let Some(mut path) = hir::Path::from_ast(path.clone()) {
if !path.is_ident() {
path.segments.pop().unwrap();
self.path_prefix = Some(path);
if let Some(path) = hir::Path::from_ast(path.clone()) {
if let Some(path_prefix) = path.qualifier() {
self.path_prefix = Some(path_prefix);
return;
}
}