mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Lay the foundation for diagnostics in ty lowering, and implement a first diagnostic
The diagnostic implemented is a simple one (E0109). It serves as a test for the new foundation. This commit only implements diagnostics for type in bodies and body-carrying signatures; the next commit will include diagnostics in the rest of the things. Also fix one weird bug that was detected when implementing this that caused `Fn::(A, B) -> C` (which is a valid, if bizarre, alternative syntax to `Fn(A, B) -> C` to lower incorrectly. And also fix a maybe-bug where parentheses were sneaked into a code string needlessly; this was not detected until now because the parentheses were removed (by the make-AST family API), but with a change in this commit they are now inserted. So fix that too.
This commit is contained in:
parent
4e475a3245
commit
5f25ae3d1b
19 changed files with 811 additions and 80 deletions
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue