aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/path/lower/lower_use.rs
blob: 0ee406f63a64dad31e60fab7aa88f79c20d1ffc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! 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;
use syntax::{ast, AstNode};

use crate::{
    db::DefDatabase,
    path::{ModPath, PathKind},
};

pub(crate) fn convert_path(
    db: &dyn DefDatabase,
    prefix: Option<ModPath>,
    path: ast::Path,
    hygiene: &Hygiene,
) -> Option<ModPath> {
    let prefix = if let Some(qual) = path.qualifier() {
        Some(convert_path(db, prefix, qual, hygiene)?)
    } else {
        prefix
    };

    let segment = path.segment()?;
    let mut mod_path = match segment.kind()? {
        ast::PathSegmentKind::Name(name_ref) => {
            match hygiene.name_ref_to_name(db.upcast(), name_ref) {
                Either::Left(name) => {
                    // no type args in use
                    let mut res = prefix.unwrap_or_else(|| {
                        ModPath::from_kind(
                            segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
                        )
                    });
                    res.segments.push(name);
                    res
                }
                Either::Right(crate_id) => {
                    return Some(ModPath::from_segments(
                        PathKind::DollarCrate(crate_id),
                        iter::empty(),
                    ))
                }
            }
        }
        ast::PathSegmentKind::CrateKw => {
            if prefix.is_some() {
                return None;
            }
            ModPath::from_segments(PathKind::Crate, iter::empty())
        }
        ast::PathSegmentKind::SelfKw => {
            if prefix.is_some() {
                return None;
            }
            ModPath::from_segments(PathKind::Super(0), iter::empty())
        }
        ast::PathSegmentKind::SuperKw => {
            let nested_super_count = match prefix.map(|p| p.kind) {
                Some(PathKind::Super(n)) => n,
                Some(_) => return None,
                None => 0,
            };

            ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
        }
        ast::PathSegmentKind::Type { .. } => {
            // not allowed in imports
            return None;
        }
    };

    // handle local_inner_macros :
    // Basically, even in rustc it is quite hacky:
    // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
    // We follow what it did anyway :)
    if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
        if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
            if let Some(crate_id) = hygiene.local_inner_macros(db.upcast(), path) {
                mod_path.kind = PathKind::DollarCrate(crate_id);
            }
        }
    }

    Some(mod_path)
}