rune/worker/
import.rs

1use core::mem::{replace, take};
2
3use crate::alloc::prelude::*;
4use crate::alloc::{self, Box, VecDeque};
5use crate::ast;
6use crate::ast::Spanned;
7use crate::compile::{DynLocation, Error, ErrorKind, Location, ModId, Result, Visibility};
8use crate::grammar::{Ignore, MaybeNode, NodeAt, Remaining, Stream, StreamBuf};
9use crate::parse::Resolve;
10use crate::query::{Query, QuerySource};
11use crate::worker::{ImportKind, Task, WildcardImport};
12use crate::{ItemBuf, SourceId};
13
14use ast::Kind::*;
15
16/// The state of an import.
17#[derive(Debug)]
18pub(crate) enum ImportState {
19    Ast(Box<ast::ItemUse>),
20    Node(NodeAt),
21    Complete,
22}
23
24/// Import to process.
25#[derive(Debug)]
26pub(crate) struct Import {
27    pub(crate) state: ImportState,
28    pub(crate) kind: ImportKind,
29    pub(crate) module: ModId,
30    pub(crate) visibility: Visibility,
31    pub(crate) item: ItemBuf,
32    pub(crate) source_id: SourceId,
33}
34
35impl Import {
36    /// Lookup a local identifier in the current context and query.
37    fn lookup_local(&self, query: &Query<'_, '_>, local: &str) -> alloc::Result<ItemBuf> {
38        let item = query.pool.module_item(self.module).extended(local)?;
39
40        if let ImportKind::Local = self.kind {
41            if query.contains_prefix(&item)? {
42                return Ok(item);
43            }
44        }
45
46        if query.context.contains_crate(local) {
47            return ItemBuf::with_crate(local);
48        }
49
50        Ok(item)
51    }
52
53    /// Process the import, populating the unit.
54    pub(crate) fn process(
55        mut self,
56        q: &mut Query<'_, '_>,
57        add_task: &mut dyn FnMut(Task) -> Result<()>,
58    ) -> Result<()> {
59        let mut q = q.with_source_id(self.source_id);
60
61        match replace(&mut self.state, ImportState::Complete) {
62            ImportState::Ast(ast) => {
63                self.process_ast(&mut q, add_task, Box::into_inner(ast))?;
64            }
65            ImportState::Node(node) => {
66                if !node.parse(|p| self.process_node(&mut q, p, add_task))? {
67                    self.kind = ImportKind::Local;
68                    self.state = ImportState::Node(node);
69
70                    if let Err(error) = add_task(Task::ExpandImport(self)) {
71                        q.error(error)?;
72                    }
73                }
74            }
75            ImportState::Complete => {}
76        }
77
78        Ok(())
79    }
80
81    fn process_ast(
82        mut self,
83        q: &mut Query<'_, '_>,
84        add_task: &mut dyn FnMut(Task) -> Result<()>,
85        ast: ast::ItemUse,
86    ) -> Result<()> {
87        let (name, first, initial) = match self.kind {
88            ImportKind::Global => {
89                match ast.path.global {
90                    Some(global) => match &ast.path.first {
91                        ast::ItemUseSegment::PathSegment(ast::PathSegment::Ident(ident)) => {
92                            let ident = ident.resolve(resolve_context!(q))?;
93                            (ItemBuf::with_crate(ident)?, None, false)
94                        }
95                        _ => {
96                            return Err(Error::new(global.span(), ErrorKind::UnsupportedGlobal));
97                        }
98                    },
99                    // NB: defer non-local imports.
100                    _ => {
101                        self.kind = ImportKind::Local;
102                        self.state = ImportState::Ast(Box::try_new(ast)?);
103                        add_task(Task::ExpandImport(self))?;
104                        return Ok(());
105                    }
106                }
107            }
108            ImportKind::Local => (ItemBuf::new(), Some(&ast.path.first), true),
109        };
110
111        let mut queue = VecDeque::new();
112
113        queue.try_push_back((&ast.path, name, first, initial))?;
114
115        while let Some((path, mut name, first, mut initial)) = queue.pop_front() {
116            tracing::trace!("process one");
117
118            let mut it = first
119                .into_iter()
120                .chain(path.segments.iter().map(|(_, s)| s));
121
122            let complete = loop {
123                let segment = match it.next() {
124                    Some(segment) => segment,
125                    None => break None,
126                };
127
128                // Only the first ever segment loaded counts as the initial
129                // segment.
130                let initial = take(&mut initial);
131
132                match segment {
133                    ast::ItemUseSegment::PathSegment(segment) => match segment {
134                        ast::PathSegment::Ident(ident) => {
135                            let ident = ident.resolve(resolve_context!(q))?;
136
137                            if !initial {
138                                name.push(ident)?;
139                                continue;
140                            }
141
142                            name = self.lookup_local(q, ident)?;
143                        }
144                        ast::PathSegment::SelfType(self_type) => {
145                            return Err(Error::new(
146                                self_type.span(),
147                                ErrorKind::ExpectedLeadingPathSegment,
148                            ));
149                        }
150                        ast::PathSegment::SelfValue(self_value) => {
151                            if !initial {
152                                return Err(Error::new(
153                                    self_value.span(),
154                                    ErrorKind::ExpectedLeadingPathSegment,
155                                ));
156                            }
157
158                            name = q.pool.module_item(self.module).try_to_owned()?;
159                        }
160                        ast::PathSegment::Crate(crate_token) => {
161                            if !initial {
162                                return Err(Error::new(
163                                    crate_token,
164                                    ErrorKind::ExpectedLeadingPathSegment,
165                                ));
166                            }
167
168                            name = ItemBuf::new();
169                        }
170                        ast::PathSegment::Super(super_token) => {
171                            if initial {
172                                name = q.pool.module_item(self.module).try_to_owned()?;
173                            }
174
175                            if !name.pop() {
176                                return Err(Error::new(super_token, ErrorKind::UnsupportedSuper));
177                            }
178                        }
179                        ast::PathSegment::Generics(arguments) => {
180                            return Err(Error::new(arguments, ErrorKind::UnsupportedGenerics));
181                        }
182                    },
183                    ast::ItemUseSegment::Wildcard(star_token) => {
184                        let mut wildcard_import = WildcardImport {
185                            visibility: self.visibility,
186                            from: self.item.try_clone()?,
187                            name: name.try_clone()?,
188                            location: Location::new(self.source_id, star_token.span()),
189                            module: self.module,
190                            found: false,
191                        };
192
193                        wildcard_import.process_global(q)?;
194                        add_task(Task::ExpandWildcardImport(wildcard_import))?;
195                        break Some(star_token.span());
196                    }
197                    ast::ItemUseSegment::Group(group) => {
198                        for (path, _) in group {
199                            if let Some(global) = &path.global {
200                                return Err(Error::new(
201                                    global.span(),
202                                    ErrorKind::UnsupportedGlobal,
203                                ));
204                            }
205
206                            queue.try_push_back((
207                                path,
208                                name.try_clone()?,
209                                Some(&path.first),
210                                initial,
211                            ))?;
212                        }
213
214                        break Some(group.span());
215                    }
216                }
217            };
218
219            if let Some(segment) = it.next() {
220                return Err(Error::new(segment, ErrorKind::IllegalUseSegment));
221            }
222
223            let alias = match &path.alias {
224                Some((_, ident)) => {
225                    if let Some(span) = complete {
226                        return Err(Error::new(
227                            span.join(ident.span()),
228                            ErrorKind::UseAliasNotSupported,
229                        ));
230                    }
231
232                    Some(*ident)
233                }
234                None => None,
235            };
236
237            if complete.is_none() {
238                q.insert_import(
239                    &DynLocation::new(self.source_id, path),
240                    self.module,
241                    self.visibility,
242                    &self.item,
243                    &name,
244                    alias,
245                    false,
246                )?;
247            }
248        }
249
250        Ok(())
251    }
252
253    fn process_node(
254        &self,
255        q: &mut QuerySource<'_, '_>,
256        p: &mut Stream<'_>,
257        add_task: &mut dyn FnMut(Task) -> Result<()>,
258    ) -> Result<bool> {
259        p.eat(Modifiers);
260        p.expect(K![use])?;
261
262        let global = p.eat(K![::]).span();
263
264        let (item, first, has_component) = match (&self.kind, global) {
265            (ImportKind::Global, Some(global)) => match p.peek() {
266                K![ident] => {
267                    let ident = p.ast::<ast::Ident>()?;
268                    let ident = ident.resolve(resolve_context!(q))?;
269                    (ItemBuf::with_crate(ident)?, false, true)
270                }
271                _ => {
272                    return Err(Error::new(global.span(), ErrorKind::UnsupportedGlobal));
273                }
274            },
275            (ImportKind::Global, None) => {
276                // Ignore remaining, since we will be processed in the global
277                // queue.
278                p.ignore();
279                return Ok(false);
280            }
281            _ => (ItemBuf::new(), true, false),
282        };
283
284        let mut queue = VecDeque::new();
285
286        queue.try_push_back((p.take_remaining(), item, first, has_component))?;
287
288        while let Some((p, item, first, has_component)) = queue.pop_front() {
289            tracing::trace!("process one");
290
291            let result = p.parse(|p| {
292                self.handle_import(
293                    q,
294                    p,
295                    item,
296                    first,
297                    has_component,
298                    add_task,
299                    &mut |p, name| queue.try_push_back((p, name, false, false)),
300                )
301            });
302
303            if let Err(error) = result {
304                q.diagnostics.error(self.source_id, error)?;
305            }
306        }
307
308        Ok(true)
309    }
310
311    fn handle_import<'a>(
312        &self,
313        q: &mut QuerySource<'_, '_>,
314        p: &mut Stream<'a>,
315        mut item: ItemBuf,
316        mut first: bool,
317        mut has_component: bool,
318        add_task: &mut dyn FnMut(Task) -> Result<()>,
319        enqueue: &mut dyn FnMut(StreamBuf<'a>, ItemBuf) -> alloc::Result<()>,
320    ) -> Result<()> {
321        let complete = loop {
322            if p.is_eof() {
323                break None;
324            }
325
326            // Only the first ever segment loaded counts as the initial
327            // segment.
328            let initial = take(&mut first);
329
330            if has_component {
331                if p.peek() == K![as] {
332                    break None;
333                }
334
335                p.expect(K![::])?;
336            }
337
338            match p.peek() {
339                K![ident] => {
340                    let ident = p.ast::<ast::Ident>()?;
341                    let ident = ident.resolve(resolve_context!(q))?;
342
343                    if !initial {
344                        item.push(ident)?;
345                    } else {
346                        item = self.lookup_local(q, ident)?;
347                    }
348                }
349                K![Self] => {
350                    let node = p.pump()?;
351
352                    return Err(Error::new(node, ErrorKind::ExpectedLeadingPathSegment));
353                }
354                K![self] => {
355                    let node = p.pump()?;
356
357                    if !initial {
358                        return Err(Error::new(node, ErrorKind::ExpectedLeadingPathSegment));
359                    }
360
361                    item = q.pool.module_item(self.module).try_to_owned()?;
362                }
363                K![crate] => {
364                    let node = p.pump()?;
365
366                    if !initial {
367                        return Err(Error::new(node, ErrorKind::ExpectedLeadingPathSegment));
368                    }
369
370                    item = ItemBuf::new();
371                }
372                K![super] => {
373                    let node = p.pump()?;
374
375                    if initial {
376                        item = q.pool.module_item(self.module).try_to_owned()?;
377                    }
378
379                    if !item.pop() {
380                        return Err(Error::new(node, ErrorKind::UnsupportedSuper));
381                    }
382                }
383                PathGenerics => {
384                    let node = p.pump()?;
385                    return Err(Error::new(node, ErrorKind::UnsupportedGenerics));
386                }
387                K![*] => {
388                    let node = p.pump()?;
389
390                    let mut wildcard_import = WildcardImport {
391                        visibility: self.visibility,
392                        from: self.item.try_clone()?,
393                        name: item.try_clone()?,
394                        location: Location::new(self.source_id, node.span()),
395                        module: self.module,
396                        found: false,
397                    };
398
399                    wildcard_import.process_global(q)?;
400
401                    if let Err(error) = add_task(Task::ExpandWildcardImport(wildcard_import)) {
402                        q.diagnostics.error(self.source_id, error)?;
403                    }
404
405                    break Some(node.span());
406                }
407                ItemUseGroup => {
408                    let node = p.pump()?;
409                    let group = Some(node.span());
410
411                    node.parse(|p| {
412                        p.expect(K!['{'])?;
413
414                        let mut comma = Remaining::default();
415
416                        while let MaybeNode::Some(node) = p.eat(ItemUsePath) {
417                            comma.exactly_one(q)?;
418
419                            node.parse(|p| {
420                                if let Some(global) = p.eat(K![::]).span() {
421                                    return Err(Error::new(global, ErrorKind::UnsupportedGlobal));
422                                }
423
424                                enqueue(p.take_remaining(), item.try_clone()?)?;
425                                Ok(())
426                            })?;
427
428                            comma = p.one(K![,]);
429                        }
430
431                        comma.at_most_one(q)?;
432                        p.expect(K!['}'])?;
433                        Ok(())
434                    })?;
435
436                    break group;
437                }
438                _ => {
439                    return Err(Error::new(p.peek_span(), ErrorKind::IllegalUseSegment));
440                }
441            }
442
443            has_component = true;
444        };
445
446        let alias = if let MaybeNode::Some(node) = p.eat(K![as]) {
447            if complete.is_some() {
448                return Err(Error::new(node, ErrorKind::UseAliasNotSupported));
449            }
450
451            Some(p.ast::<ast::Ident>()?)
452        } else {
453            None
454        };
455
456        if let Some(span) = p.remaining_span() {
457            return Err(Error::new(span, ErrorKind::IllegalUseSegment));
458        }
459
460        if complete.is_none() {
461            q.insert_import(
462                &DynLocation::new(self.source_id, p),
463                self.module,
464                self.visibility,
465                &self.item,
466                &item,
467                alias,
468                false,
469            )?;
470        }
471
472        Ok(())
473    }
474}