aboutsummaryrefslogtreecommitdiff
path: root/crates/parser/src/grammar/items/use_item.rs
blob: 20e6a13cf96da0933c2ce7947ec1d160e6c0a4b0 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! FIXME: write short doc here

use super::*;

pub(super) fn use_(p: &mut Parser, m: Marker) {
    assert!(p.at(T![use]));
    p.bump(T![use]);
    use_tree(p, true);
    p.expect(T![;]);
    m.complete(p, USE);
}

/// Parse a use 'tree', such as `some::path` in `use some::path;`
/// Note that this is called both by `use_item` and `use_tree_list`,
/// so handles both `some::path::{inner::path}` and `inner::path` in
/// `use some::path::{inner::path};`
fn use_tree(p: &mut Parser, top_level: bool) {
    let m = p.start();
    match p.current() {
        // Finish the use_tree for cases of e.g.
        // `use some::path::{self, *};` or `use *;`
        // This does not handle cases such as `use some::path::*`
        // N.B. in Rust 2015 `use *;` imports all from crate root
        // however in Rust 2018 `use *;` errors: ('cannot glob-import all possible crates')
        // FIXME: Add this error (if not out of scope)

        // test use_star
        // use *;
        // use ::*;
        // use some::path::{*};
        // use some::path::{::*};
        T![*] => p.bump(T![*]),
        T![:] if p.at(T![::]) && p.nth(2) == T![*] => {
            // Parse `use ::*;`, which imports all from the crate root in Rust 2015
            // This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`)
            // but still parses and errors later: ('crate root in paths can only be used in start position')
            // FIXME: Add this error (if not out of scope)
            // In Rust 2018, it is always invalid (see above)
            p.bump(T![::]);
            p.bump(T![*]);
        }
        // Open a use tree list
        // Handles cases such as `use {some::path};` or `{inner::path}` in
        // `use some::path::{{inner::path}, other::path}`

        // test use_tree_list
        // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
        // use {path::from::root}; // Rust 2015
        // use ::{some::arbritrary::path}; // Rust 2015
        // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
        T!['{'] => {
            use_tree_list(p);
        }
        T![:] if p.at(T![::]) && p.nth(2) == T!['{'] => {
            p.bump(T![::]);
            use_tree_list(p);
        }
        // Parse a 'standard' path.
        // Also handles aliases (e.g. `use something as something_else`)

        // test use_path
        // use ::crate_name; // Rust 2018 - All flavours
        // use crate_name; // Rust 2018 - Anchored paths
        // use item_in_scope_or_crate_name; // Rust 2018 - Uniform Paths
        //
        // use self::module::Item;
        // use crate::Item;
        // use self::some::Struct;
        // use crate_name::some_item;
        _ if paths::is_use_path_start(p) => {
            paths::use_path(p);
            match p.current() {
                T![as] => {
                    // test use_alias
                    // use some::path as some_name;
                    // use some::{
                    //  other::path as some_other_name,
                    //  different::path as different_name,
                    //  yet::another::path,
                    //  running::out::of::synonyms::for_::different::*
                    // };
                    // use Trait as _;
                    opt_rename(p);
                }
                T![:] if p.at(T![::]) => {
                    p.bump(T![::]);
                    match p.current() {
                        T![*] => {
                            p.bump(T![*]);
                        }
                        // test use_tree_list_after_path
                        // use crate::{Item};
                        // use self::{Item};
                        T!['{'] => use_tree_list(p),
                        _ => {
                            // is this unreachable?
                            p.error("expected `{` or `*`");
                        }
                    }
                }
                _ => (),
            }
        }
        _ => {
            m.abandon(p);
            let msg = "expected one of `*`, `::`, `{`, `self`, `super` or an identifier";
            if top_level {
                p.err_recover(msg, ITEM_RECOVERY_SET);
            } else {
                // if we are parsing a nested tree, we have to eat a token to
                // main balanced `{}`
                p.err_and_bump(msg);
            }
            return;
        }
    }
    m.complete(p, USE_TREE);
}

pub(crate) fn use_tree_list(p: &mut Parser) {
    assert!(p.at(T!['{']));
    let m = p.start();
    p.bump(T!['{']);
    while !p.at(EOF) && !p.at(T!['}']) {
        use_tree(p, false);
        if !p.at(T!['}']) {
            p.expect(T![,]);
        }
    }
    p.expect(T!['}']);
    m.complete(p, USE_TREE_LIST);
}