aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/grammar/items/traits.rs
blob: 25b6cb66b3d29e550ed43948cf9960b6c6dabebf (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
use super::*;

// test trait_item
// trait T<U>: Hash + Clone where U: Copy {}
pub(super) fn trait_def(p: &mut Parser) {
    assert!(p.at(TRAIT_KW));
    p.bump();
    name_r(p, ITEM_RECOVERY_SET);
    type_params::opt_type_param_list(p);
    if p.at(COLON) {
        type_params::bounds(p);
    }
    type_params::opt_where_clause(p);
    if p.at(L_CURLY) {
        trait_item_list(p);
    } else {
        p.error("expected `{`");
    }
}

// test trait_item_list
// impl F {
//     type A: Clone;
//     const B: i32;
//     fn foo() {}
//     fn bar(&self);
// }
pub(crate) fn trait_item_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.bump();
    while !p.at(EOF) && !p.at(R_CURLY) {
        if p.at(L_CURLY) {
            error_block(p, "expected an item");
            continue;
        }
        item_or_macro(p, true, ItemFlavor::Trait);
    }
    p.expect(R_CURLY);
    m.complete(p, ITEM_LIST);
}

// test impl_item
// impl Foo {}
pub(super) fn impl_item(p: &mut Parser) {
    assert!(p.at(IMPL_KW));
    p.bump();
    if choose_type_params_over_qpath(p) {
        type_params::opt_type_param_list(p);
    }

    // TODO: never type
    // impl ! {}

    // test impl_item_neg
    // impl !Send for X {}
    if p.at(IMPL_KW) {
        p.error("expected type");
    } else {
        p.eat(EXCL);
        if p.at(IMPL_KW) {
            p.error("expected type");
        } else {
            types::type_(p);
        }
    }
    if p.eat(FOR_KW) {
        types::type_(p);
    }
    type_params::opt_where_clause(p);
    if p.at(L_CURLY) {
        impl_item_list(p);
    } else {
        p.error("expected `{`");
    }
}

// test impl_item_list
// impl F {
//     type A = i32;
//     const B: i32 = 92;
//     fn foo() {}
//     fn bar(&self) {}
// }
pub(crate) fn impl_item_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.bump();

    while !p.at(EOF) && !p.at(R_CURLY) {
        if p.at(L_CURLY) {
            error_block(p, "expected an item");
            continue;
        }
        item_or_macro(p, true, ItemFlavor::Mod);
    }
    p.expect(R_CURLY);
    m.complete(p, ITEM_LIST);
}

fn choose_type_params_over_qpath(p: &Parser) -> bool {
    // There's an ambiguity between generic parameters and qualified paths in impls.
    // If we see `<` it may start both, so we have to inspect some following tokens.
    // The following combinations can only start generics,
    // but not qualified paths (with one exception):
    //     `<` `>` - empty generic parameters
    //     `<` `#` - generic parameters with attributes
    //     `<` (LIFETIME|IDENT) `>` - single generic parameter
    //     `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
    //     `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
    //     `<` (LIFETIME|IDENT) `=` - generic parameter with a default
    // The only truly ambiguous case is
    //     `<` IDENT `>` `::` IDENT ...
    // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
    // because this is what almost always expected in practice, qualified paths in impls
    // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
    if !p.at(L_ANGLE) {
        return false;
    }
    if p.nth(1) == POUND || p.nth(1) == R_ANGLE {
        return true;
    }
    (p.nth(1) == LIFETIME || p.nth(1) == IDENT)
        && (p.nth(2) == R_ANGLE || p.nth(2) == COMMA || p.nth(2) == COLON || p.nth(2) == EQ)
}