summaryrefslogtreecommitdiff
path: root/src/parse.rs
blob: 546aa7c2177597ccb082b013ed156d7589ebfac7 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
use proc_macro2::{Delimiter, Span, TokenTree};
use quote::format_ident;
use syn::{spanned::Spanned, Attribute, Error, Fields, GenericParam, ItemStruct};

use crate::{
    covariance_detection::type_is_covariant_over_this_lifetime,
    info_structures::{BorrowRequest, Derive, FieldType, StructFieldInfo, StructInfo},
    utils::submodule_contents_visiblity,
};

fn handle_borrows_attr(
    field_info: &mut [StructFieldInfo],
    attr: &Attribute,
    borrows: &mut Vec<BorrowRequest>,
) -> Result<(), Error> {
    let mut borrow_mut = false;
    let mut waiting_for_comma = false;
    let tokens = attr.tokens.clone();
    let possible_error = Error::new_spanned(&tokens, "Invalid syntax for borrows() macro.");
    let tokens = if let Some(TokenTree::Group(group)) = tokens.into_iter().next() {
        group.stream()
    } else {
        return Err(possible_error);
    };
    for token in tokens {
        if let TokenTree::Ident(ident) = token {
            if waiting_for_comma {
                return Err(Error::new_spanned(&ident, "Expected comma."));
            }
            let istr = ident.to_string();
            if istr == "mut" {
                if borrow_mut {
                    return Err(Error::new_spanned(&ident, "Unexpected double 'mut'"));
                }
                borrow_mut = true;
            } else {
                let index = field_info.iter().position(|item| item.name == istr);
                let index = if let Some(v) = index {
                    v
                } else {
                    return Err(Error::new_spanned(
                        &ident,
                        concat!(
                            "Unknown identifier, make sure that it is spelled ",
                            "correctly and defined above the location it is borrowed."
                        ),
                    ));
                };
                if borrow_mut {
                    if field_info[index].field_type == FieldType::Borrowed {
                        return Err(Error::new_spanned(
                            &ident,
                            "Cannot borrow mutably, this field was previously borrowed immutably.",
                        ));
                    }
                    if field_info[index].field_type == FieldType::BorrowedMut {
                        return Err(Error::new_spanned(&ident, "Cannot borrow mutably twice."));
                    }
                    field_info[index].field_type = FieldType::BorrowedMut;
                } else {
                    if field_info[index].field_type == FieldType::BorrowedMut {
                        return Err(Error::new_spanned(
                            &ident,
                            "Cannot borrow as immutable as it was previously borrowed mutably.",
                        ));
                    }
                    field_info[index].field_type = FieldType::Borrowed;
                }
                borrows.push(BorrowRequest {
                    index,
                    mutable: borrow_mut,
                });
                waiting_for_comma = true;
                borrow_mut = false;
            }
        } else if let TokenTree::Punct(punct) = token {
            if punct.as_char() == ',' {
                if waiting_for_comma {
                    waiting_for_comma = false;
                } else {
                    return Err(Error::new_spanned(&punct, "Unexpected extra comma."));
                }
            } else {
                return Err(Error::new_spanned(
                    &punct,
                    "Unexpected punctuation, expected comma or identifier.",
                ));
            }
        } else {
            return Err(Error::new_spanned(
                &token,
                "Unexpected token, expected comma or identifier.",
            ));
        }
    }
    Ok(())
}

fn parse_derive_token(token: &TokenTree) -> Result<Option<Derive>, Error> {
    match token {
        TokenTree::Ident(ident) => match &ident.to_string()[..] {
            "Debug" => Ok(Some(Derive::Debug)),
            "PartialEq" => Ok(Some(Derive::PartialEq)),
            "Eq" => Ok(Some(Derive::Eq)),
            _ => Err(Error::new(
                ident.span(),
                format!("{} cannot be derived for self-referencing structs", ident),
            )),
        },
        TokenTree::Punct(..) => Ok(None),
        _ => Err(Error::new(token.span(), "bad syntax")),
    }
}

fn parse_derive_attribute(attr: &Attribute) -> Result<Vec<Derive>, Error> {
    let body = &attr.tokens;
    if let Some(TokenTree::Group(body)) = body.clone().into_iter().next() {
        if body.delimiter() != Delimiter::Parenthesis {
            panic!("TODO: nice error, bad define syntax")
        }
        let mut derives = Vec::new();
        for token in body.stream().into_iter() {
            if let Some(derive) = parse_derive_token(&token)? {
                derives.push(derive);
            }
        }
        Ok(derives)
    } else {
        Err(Error::new(attr.span(), "bad syntax"))
    }
}

pub fn parse_struct(def: &ItemStruct) -> Result<StructInfo, Error> {
    let vis = def.vis.clone();
    let generics = def.generics.clone();
    let mut actual_struct_def = def.clone();
    actual_struct_def.vis = vis.clone();
    let mut fields = Vec::new();
    match &mut actual_struct_def.fields {
        Fields::Named(def_fields) => {
            for field in &mut def_fields.named {
                let mut borrows = Vec::new();
                let mut self_referencing = false;
                let mut covariant = type_is_covariant_over_this_lifetime(&field.ty);
                let mut remove_attrs = Vec::new();
                for (index, attr) in field.attrs.iter().enumerate() {
                    let path = &attr.path;
                    if path.leading_colon.is_some() {
                        continue;
                    }
                    if path.segments.len() != 1 {
                        continue;
                    }
                    if path.segments.first().unwrap().ident == "borrows" {
                        if self_referencing {
                            panic!("TODO: Nice error, used #[borrows()] twice.");
                        }
                        self_referencing = true;
                        handle_borrows_attr(&mut fields[..], attr, &mut borrows)?;
                        remove_attrs.push(index);
                    }
                    if path.segments.first().unwrap().ident == "covariant" {
                        if covariant.is_some() {
                            panic!("TODO: Nice error, covariance specified twice.");
                        }
                        covariant = Some(true);
                        remove_attrs.push(index);
                    }
                    if path.segments.first().unwrap().ident == "not_covariant" {
                        if covariant.is_some() {
                            panic!("TODO: Nice error, covariance specified twice.");
                        }
                        covariant = Some(false);
                        remove_attrs.push(index);
                    }
                }
                // We should not be able to access the field outside of the hidden module where
                // everything is generated.
                let with_vis = submodule_contents_visiblity(&field.vis.clone());
                fields.push(StructFieldInfo {
                    name: field.ident.clone().expect("Named field has no name."),
                    typ: field.ty.clone(),
                    field_type: FieldType::Tail,
                    vis: with_vis,
                    borrows,
                    self_referencing,
                    covariant,
                });
            }
        }
        Fields::Unnamed(_fields) => {
            return Err(Error::new(
                Span::call_site(),
                "Tuple structs are not supported yet.",
            ))
        }
        Fields::Unit => {
            return Err(Error::new(
                Span::call_site(),
                "Unit structs cannot be self-referential.",
            ))
        }
    }
    if fields.len() < 2 {
        return Err(Error::new(
            Span::call_site(),
            "Self-referencing structs must have at least 2 fields.",
        ));
    }
    let mut has_non_tail = false;
    for field in &fields {
        if !field.field_type.is_tail() {
            has_non_tail = true;
            break;
        }
    }
    if !has_non_tail {
        return Err(Error::new(
            Span::call_site(),
            &format!(
                concat!(
                    "Self-referencing struct cannot be made entirely of tail fields, try adding ",
                    "#[borrows({0})] to a field defined after {0}."
                ),
                fields[0].name
            ),
        ));
    }
    let first_lifetime = if let Some(GenericParam::Lifetime(param)) = generics.params.first() {
        param.lifetime.ident.clone()
    } else {
        format_ident!("static")
    };
    let mut attributes = Vec::new();
    let mut derives = Vec::new();
    for attr in &def.attrs {
        let p = &attr.path.segments;
        if p.len() == 0 {
            return Err(Error::new(p.span(), &format!("Unsupported attribute")));
        }
        let name = p[0].ident.to_string();
        let good = match &name[..] {
            "clippy" | "allow" | "deny" | "doc" => true,
            _ => false,
        };
        if good {
            attributes.push(attr.clone())
        } else if name == "derive" {
            if derives.len() > 0 {
                return Err(Error::new(
                    attr.span(),
                    "Multiple derive attributes not allowed",
                ));
            } else {
                derives = parse_derive_attribute(attr)?;
            }
        } else {
            return Err(Error::new(p.span(), &format!("Unsupported attribute")));
        }
    }

    return Ok(StructInfo {
        derives,
        ident: def.ident.clone(),
        generics: def.generics.clone(),
        fields,
        vis,
        first_lifetime,
        attributes,
    });
}