Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/path/lower.rs')
-rw-r--r--crates/hir-def/src/path/lower.rs88
1 files changed, 82 insertions, 6 deletions
diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index 553e615b94..3b7e7653fb 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -17,13 +17,31 @@ use crate::{
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
+#[cfg(test)]
+thread_local! {
+ /// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
+ pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
+}
+
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
+// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
+// also needs an update.
pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut type_anchor = None;
let mut segments = Vec::new();
let mut generic_args = Vec::new();
+ #[cfg(test)]
+ let mut ast_segments = Vec::new();
+ #[cfg(test)]
+ let mut ast_segments_offset = 0;
+ #[allow(unused_mut)]
+ let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
+ #[cfg(test)]
+ ast_segments.push(_segment.clone());
+ segments.push(name);
+ };
loop {
let segment = path.segment()?;
@@ -34,6 +52,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => {
if name_ref.text() == "$crate" {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
break kind = resolve_crate_root(
ctx.db.upcast(),
ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx,
@@ -48,7 +70,7 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
.or_else(|| {
lower_generic_args_from_fn_path(
ctx,
- segment.param_list(),
+ segment.parenthesized_arg_list(),
segment.ret_type(),
)
});
@@ -56,10 +78,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
generic_args.resize(segments.len(), None);
generic_args.push(args);
}
- segments.push(name);
+ push_segment(&segment, &mut segments, name);
}
ast::PathSegmentKind::SelfTypeKw => {
- segments.push(Name::new_symbol_root(sym::Self_.clone()));
+ push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_.clone()));
}
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
@@ -81,6 +103,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
kind = mod_path.kind;
segments.extend(mod_path.segments().iter().cloned().rev());
+ #[cfg(test)]
+ {
+ ast_segments_offset = mod_path.segments().len();
+ }
if let Some(path_generic_args) = path_generic_args {
generic_args.resize(segments.len() - num_segments, None);
generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
@@ -112,10 +138,18 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
ast::PathSegmentKind::CrateKw => {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
kind = PathKind::Crate;
break;
}
ast::PathSegmentKind::SelfKw => {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
// don't break out if `self` is the last segment of a path, this mean we got a
// use tree like `foo::{self}` which we want to resolve as `foo`
if !segments.is_empty() {
@@ -162,6 +196,13 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
+ #[cfg(test)]
+ {
+ ast_segments.reverse();
+ SEGMENT_LOWERING_MAP
+ .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
+ }
+
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
if type_anchor.is_none() && generic_args.is_empty() {
return Some(Path::BarePath(mod_path));
@@ -181,6 +222,41 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
+/// This function finds the AST segment that corresponds to the HIR segment
+/// with index `segment_idx` on the path that is lowered from `path`.
+pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
+ // Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
+ // as keeping source maps for all paths segments will have a severe impact on memory usage.
+
+ let mut segments = path.segments();
+ if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
+ segments.clone().next().and_then(|it| it.kind())
+ {
+ segments.next();
+ return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
+ }
+ return find_segment(segments, segment_idx);
+
+ fn find_segment(
+ segments: impl Iterator<Item = ast::PathSegment>,
+ segment_idx: u32,
+ ) -> Option<ast::PathSegment> {
+ segments
+ .filter(|segment| match segment.kind() {
+ Some(
+ ast::PathSegmentKind::CrateKw
+ | ast::PathSegmentKind::SelfKw
+ | ast::PathSegmentKind::SuperKw
+ | ast::PathSegmentKind::Type { .. },
+ )
+ | None => false,
+ Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
+ Some(ast::PathSegmentKind::SelfTypeKw) => true,
+ })
+ .nth(segment_idx as usize)
+ }
+}
+
pub(super) fn lower_generic_args(
lower_ctx: &mut LowerCtx<'_>,
node: ast::GenericArgList,
@@ -247,12 +323,12 @@ pub(super) fn lower_generic_args(
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
fn lower_generic_args_from_fn_path(
ctx: &mut LowerCtx<'_>,
- params: Option<ast::ParamList>,
+ args: Option<ast::ParenthesizedArgList>,
ret_type: Option<ast::RetType>,
) -> Option<GenericArgs> {
- let params = params?;
+ let params = args?;
let mut param_types = Vec::new();
- for param in params.params() {
+ for param in params.type_args() {
let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
param_types.push(type_ref);
}